If you couldn’t tell before, I think promises are a great abstraction for doing complex asynchronous work in Javascript.
On a recent project, I needed to poll an endpoint using AJAX repeatedly (until I got the response I wanted). This can be useful as a poor man’s long polling1, for processes that take a long time for the server to perform. In this case, the server would respond with {status: "processing"}
until it finished performing a task. Then it would respond with {status: "success"}
or {status: "failure"}
when the task was done. So, we’d need to poll every 10 seconds until it returned “success” or “failure.” This is essentially an asynchronous do-while loop, and the cool thing is, it can be completely encapsulated into a single promise:
function asyncDoWhile(delay, fns, promise) {
var prom = promise || $.Deferred(),
doFn = fns.evalFn,
successPredicateFn = fns.isSuccess || function() { return true; },
failurePredicateFn = fns.isFailure || function() { return false; },
timeout;
doFn()
.done(function() {
if (successPredicateFn.apply(this, arguments)) {
prom.resolve.apply(prom, arguments);
} else if (failurePredicateFn.apply(this, arguments)) {
prom.reject.apply(prom, arguments);
} else {
timeout = setTimeout(function() {
asyncDoWhile(delay, fns, prom);
}, delay);
}
})
.fail(function() { prom.reject.apply(prom, arguments); });
var result = prom.promise();
result.abort = function() {
clearTimeout(timeout);
};
return promise;
};
Use it like this:
asyncDoWhile(10000, {
// a function that returns a promise
evalFn: function() {
return $.get('/some/api_endpoint');
},
// the results of evalFn are passed through these functions
isSuccess: function(data) {
return data.status === 'success';
},
isFailure: function(data) {
return data.status === 'failed';
}
}).then(function(data) {
// do something with the data
});
This will poll /some/api_endpoint
every 10 seconds as long as neither isSuccess
nor isFailure
return true. asyncDoWhile
returns a promise, too, which means you can chain it with then
, for when you are done polling and want to show a success message, for example.
-
Of course, if you’re depending a lot on this type of behavior in production, it might be worth it to just implement long polling or use websockets or something. ↩