-
-
Save jakearchibald/785f79b0dea5bfe0c448 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Attempt to fetch data from the cache | |
var cachedFetch = fetchGalleryData({ | |
useCache: true | |
}); | |
// Attempt to update from the network at the same time | |
showSpinner(); | |
var liveFetchResolved = false; | |
var liveUpdate = fetchGalleryData({ | |
useCache: false | |
}).then(function(data) { | |
liveFetchResolved = true; | |
updateGallery(data); | |
}) | |
// always hide the spinner after network activity | |
.then(hideSpinner, hideSpinner); | |
// React to the cached fetch: | |
var cachedUpdate = cachedFetch.then(function(data) { | |
// Don't update from cache if network won | |
// Using a var for this doesn't feel Promise-like | |
if (!liveFetchResolved) { | |
updateGallery(data); | |
} | |
}); | |
// Cater for no cache or live data | |
// "race" feels like the wrong method name for this usage | |
// TODO: this doesn't do what I think it does | |
// A) because my use of hideSpinner means liveUpdate never fails | |
// B) race will reject on the first rejection, not if all reject | |
Promise.race([cachedUpdate, liveUpdate]).catch(showNoDataError); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Here are loose definitions for the functions above | |
// you don't really need to read this unless the above is confusing | |
function fetchGalleryData(opts) { | |
return new Promise(function(resolve, reject) { | |
var xhr = new XMLHttpRequest(); | |
xhr.open('get', 'http://api.example.com/gallery.json'); | |
xhr.responseType = 'json'; | |
if (opts.useCache) { | |
// we'll pick this up in the ServiceWorker | |
xhr.setRequestHeader('x-use-cache', 'true'); | |
} | |
xhr.onload = function() { | |
resolve(xhr.response); | |
}; | |
xhr.onabort = xhr.ontimeout = xhr.onerror = reject; | |
}); | |
} | |
var gallery = document.querySelector('.gallery'); | |
function updateGallery(data) { | |
gallery.innerHTML = data.html; | |
} | |
var spinner = document.querySelector('.spinner'); | |
function showSpinner() { | |
spinner.style.display = 'none'; | |
} | |
function hideSpinner() { | |
spinner.style.display = 'none'; | |
} | |
var noDataError = document.querySelector('.no-data-error'); | |
function showNoDataError() { | |
noDataError.style.display = 'block'; | |
} |
For completeness, here's the solution using today's Promises and ES5:
var updatedFromFresh = false;
showSpinner();
var cacheUpdate = fetchGalleryData({ useCache: true })
.then(function(data) {
if (!updatedFromFresh) {
updateGallery(data);
}
});
var freshUpdate = fetchGalleryData({ useCache: false })
.then(function(data) {
updateGallery(data);
updatedFromFresh = true;
});
cacheUpdate.catch(function() {
return freshUpdate;
}).catch(showNoDataError).then(hideSpinner);
There's a small behaviour change: hideSpinner is called when both the cache & fresh update has resolved/rejected. The previous example may hide the spinner before the cache update has happened, which is likely if the user has no connection at all.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In that case, using
finally
as in domenic/promises-unwrapping#18 and a customany
function:Note that it's important to use
.finally(hideSpinner)
instead of.then(hideSpinner, hideSpinner)
, since.finally
propagates the rejection, so that if the fresh update fails,freshUpdate
is rejected. This is important to ensure that if both updates fail,showNoDataError
is called; if we did.then(hideSpinner, hideSpinner)
,freshUpdate
would always fulfill.