Reputation: 81
I've been playing with promises and trying to build some sort of progress notification.
The code is executing all functions in the right order, but the progress updates execute just before the resolve as opposed to when they actually happen.
Can anyone point out what I'm doing wrong?
function start(x) {
console.log("Start: " + x);
var promise = process(x);
console.log("promise returned");
promise.then(function(data) {
console.log("Completed: " + data);
}, function(data) {
console.log("Cancelled: " + data);
}, function(data) {
console.log("In Progress: " + data);
});
}
function process(x) {
var deferred = $.Deferred();
var promise = deferred.promise();
// process asynchronously
setTimeout(function() {
for (var i=0 ; i<x ; i++) {
sleep(1000);
deferred.notify(i);
}
if (x % 2 === 0) {
deferred.reject(x);
} else {
deferred.resolve(x);
}
}, 0);
return promise;
}
function sleep(sleepDuration) {
var now = new Date().getTime();
while(new Date().getTime() < now + sleepDuration){ /* do nothing */ }
}
start(3);
Fiddle here: https://jsfiddle.net/n86mr9tL/
Upvotes: 1
Views: 601
Reputation: 19288
A delay timer implemented with while()
, will "block" - ie hog the processor.
Blocking not only prevents other javascript from running but also inhibits reliable refreshing of the browser screen including the console. So whereas those deferred.notify(i)
and console.log("In Progress: " + data)
statements are firing, the console isn't refreshed until the processor becomes free to do so.
Unsurprisingly, the solution lies in not using while()
.
Fortunately, javascript imcludes two built-in methods window.setTimeout()
and window.setInterval()
, which are conceptually different from a while()
idler but fulfil the same role .... without blocking.
window.setInterval(fn, t)
fires function fn
every t milliseconds,window.setTimeout(fn, t)
fires function fn
once, after t milliseconds.Both methods return an opaque reference, which allows them to be cancelled.
In the code below, start()
is unmodified, process()
is heavily modified and sleep()
has disappeared.
process()
now does the following :
setInterval()
of 1000 milliseconds (1 second), whose function :
deferred.notify()
every second until the counter i
reaches the specified maximum x
,deferred.resolve()
or deferred.reject()
are called to settle the Deferred (and its promise),function start(x) {
console.log("Start: " + x);
process(x).then(function(data) {
console.log("Completed: " + data);
}, function(data) {
console.log("Cancelled: " + data);
}, function(data) {
console.log("In Progress: " + data);
});
}
function process(x) {
return $.Deferred(function(dfd) {
var i = 1;
var intervalRef = setInterval(function() {
if(i < x) {
dfd.notify(i++);
} else {
clearInterval(intervalRef);
dfd[(x % 2 === 0)?'reject':'resolve'](x);
}
}, 1000);
}).promise();
}
console.clear();
start(3);
Upvotes: 1