How To Implement Cancellable, Ordered Promises?
Solution 1:
Instead of using debounce, or timeouts, I set a small amount of state outside inside (suggestion by Jaromanda X) of this function that uses a referenced function. This way, you can just change the function reference to something like a noop
. The promise still resolves, but it won't take any action. However, the final one will not have changed its function reference:
var onInput = function() {
let logger = function(term, results) {
console.log(`results for "${term}"`, results);
};
let noop = Function.prototype;
let lastInstance = null;
functionActionManager(action) {
this.action = action;
}
returnfunctiononInput(ev) {
let term = ev.target.value;
console.log(`searching for "${term}"`);
if (lastInstance) {
lastInstance.action = noop;
}
let inst = newActionManager(logger.bind(null, term));
lastInstance = inst;
getSearchResults(term).then(response => inst.action(response));
}
}();
/****************************************
* The rest of the JavaScript is included only for simulation purposes
****************************************/functiongetSearchResults(term) {
returnnewPromise((resolve, reject) => {
let timeout = getRandomIntInclusive(100, 2000);
setTimeout(() => {
resolve([term.toLowerCase(), term.toUpperCase()]);
}, timeout);
});
}
functiongetRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
returnMath.floor(Math.random() * (max - min + 1)) + min;
}
<inputonInput="onInput(event)">
Solution 2:
You can use Promise.race
to cancel the effect of a previous chain:
letcancel = () => {};
functiononInput(ev) {
let term = ev.target.value;
console.log(`searching for "${term}"`);
cancel();
let p = newPromise(resolve => cancel = resolve);
Promise.race([p, getSearchResults(term)]).then(results => {
if (results) {
console.log(`results for "${term}"`,results);
}
});
}
functiongetSearchResults(term) {
returnnewPromise(resolve => {
let timeout = 100 + Math.floor(Math.random() * 1900);
setTimeout(() =>resolve([term.toLowerCase(), term.toUpperCase()]), timeout);
});
}
<inputonInput="onInput(event)">
Here we're doing it by injecting an undefined
result and testing for it.
Solution 3:
One workable solution is to include a latestTimestamp
and simply ignore any responses that come in with an early timestamp (and and therefore obsolete).
let latestTimestamp = 0;
functiononInput(ev) {
let term = ev.target.value;
console.log(`searching for "${term}"`);
latestTimestamp = Date.now();
getSearchResults(term, latestTimestamp).then(results => {
if (results[2] !== latestTimestamp) {
console.log("Ignoring old answer");
} else {
console.log(`results for "${term}"`, results);
}
});
}
functiongetSearchResults(term, latestTimestamp) {
returnnewPromise((resolve, reject) => {
let timeout = getRandomIntInclusive(100, 2000);
setTimeout(() => {
resolve([term.toLowerCase(), term.toUpperCase(), latestTimestamp]);
}, timeout);
});
}
functiongetRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
returnMath.floor(Math.random() * (max - min + 1)) + min;
}
<inputonInput="onInput(event)">
Solution 4:
You shouldn't use setTimeout
's in promises the way you are doing it, because from the .then
you are returning the callback from the .setTimeout()
which would not work and mess up the order. To make the promises go in order you should make a function like shown below:
functionwait(n){
returnnewPromise(function(resolve){
setTimeout(resolve, n)
});
}
and substitute the setTimeout()
's with that function like shown below:
wait(getRandomIntInclusive(100,2000)).then(function(){
// code
});
Solution 5:
You can use async
package - a bunch of utilities to maintain asynchronous code. It was first developed for node.js but it can also be used in frontend.
You need series
function, it saves an order of promises. Here is a brief example in coffeescript:
async.series([
->
### do some stuff ###
Q 'one'
->
### do some more stuff ... ###
Q 'two'
]).then (results) ->
### results is now equal to ['one', 'two'] ###
doStuff()
.done()
### an example using an object instead of an array ###
async.series({
one: -> Q.delay(200).thenResolve(1)
two: -> Q.delay(100).thenResolve(2)
}).then (results) ->
### results is now equal to: {one: 1, two: 2} ###
doStuff()
.done()
See caolan.github.io/async/
Post a Comment for "How To Implement Cancellable, Ordered Promises?"