I want to ask how exactly promises in javascript work. I am having hard time to understand difference between execution of chain composed of jQuery.animate and setTimeout.
If I do something like this:
var promise = new Promise(function(resolve, reject) {
resolve(
$("#something").animate({
width: 100
}, 1000);
)
});
promise.then(function(data) {
$("#something").animate({
height: 100
}, 1000);
});
Then the second animation starts after the first one is completed (which is wanted behavior). On the other hand, when I change .then
(replace .animate
) with e.g. setTimeout
then it is executed right after the animation is started (it does not wait for animation to end).
promise.then(function(data) {
setTimeout(function() {
console.log("timeout started");
}, 1000);
});
So my question is: why the first scenario works fine and the second does not? I thought that asynchronous tasks are handled in the same way. And how should I change the code to properly chain any functions (to wait for the previous ones to execute).
First off, jQuery will serialize animations on the same object for you via it's built-in animation queue. You don't need to use any promises in order to do that.
So, if you want your two animations to be one after the other, you can simply do this without any use of promises:
$("#something").animate({width: 100}, 1000).animate({height: 100}, 1000);
Then, if you want to use promises with jQuery animations, you have to obtain the promise for the animation like this:
$("#something").animate({width: 100}, 1000).promise();
and then you can use that promise with .then()
.
If you wanted to use jQuery promises to control the animations, you could do this:
$("#something").animate({width: 100}, 1000).promise().then(function() {
$("#something").animate({height: 100}, 1000);
});
Just calling:
$("#something").animate({width: 100}, 1000);
in your original code returns a jQuery object which is not something a promise can use by itself.
If you really wanted to create your own promise object and resolve it yourself (something that is not required with jQuery animations and not recommended when there's already a promise that you can use), you could so do like this where you use the completion function from the animation to resolve your promise (using your coding style):
var promise = new Promise(function(resolve, reject) {
$("#something").animate({width: 100}, 1000, resolve);
});
promise.then(function(data) {
console.log("timeout started");
setTimeout(function() {
console.log("timeout finished");
}, 1000);
});
So, now to explain what was going on in your code. In your first example, your promise logic wasn't working at all (it was pretty much just coded wrong). So, both animations were getting called immediately, but because jQuery queues them to run sequentially for you, that's the behavior you saw. The sequencing had nothing to do with your broken promise logic.
Then, when you put in the setTimeout()
for the second operation, jQuery was no longer queuing that for you because it's not a jQuery animation by itself and your promise logic was still broken so both operations executed at the same time.
If you really want to just chain timeouts, then you can make a wrapper that combines a timeout with a promise and that can be chainable:
function timer(t) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, t);
});
}
timer(1000).then(function() {
// do something here
return timer(1000);
}).then(function() {
// do something here
});
Working demo: http://jsfiddle.net/jfriend00/85Pr6/ (requires browser that has promises built-in)