Why are Javascript promises resolving out of order?

Tags: ,



I am attempting to use JavaScript promises in a project and the order of events are unexpected. I have narrowed it down into a small demo using a test promise.

testPromises = function(promiseNum){
    return new Promise(function(resolve, reject) {
        console.log ('handling promise '+promiseNum);
        setTimeout(function(){
            console.log("resolving testPromises "+promiseNum);
            resolve();
        },2000)
    });
};

And then I call it like so:

testPromises(1).then(testPromises(2)).then(testPromises(3))
.then(function(value) {
        console.log('all promises resolved');
}, function(reason) {
        console.log('some promise was rejected');
});

This is the console output:

handling promise 1
handling promise 2
handling promise 3
resolving testPromises 1
all promises resolved
resolving testPromises 2
resolving testPromises 3

How can I get an output of:

handling promise 1
resolving testPromises 1
handling promise 2
resolving testPromises 2
handling promise 3
resolving testPromises 3
all promises resolved

Answer

.then() expects a function reference. When you do something like this:

.then(testPromises(2))

you are executing the function testPromise() immediately and passing the return value to .then(). This is almost never what you want (unless testPromises() returned another function) because for .then() to do its job, you MUST pass it a function reference (a function that it can call sometime later). If you execute the function immediately, then .then() can’t do its job and call it LATER.

Instead, what you want is this:

.then(function() {
    return testPromises(2);
})

Or, you could use .bind():

.then(testPromises.bind(null, 2))

So, your whole chain would look like this:

testPromises(1).then(function() {
    return testPromises(2);
}).then(function() {
    return testPromises(3);
}).then(function(value) {
    console.log('all promises resolved');
}, function(reason) {
    console.log('some promise was rejected');
});

Or, using .bind()

testPromises(1)
  .then(testPromises.bind(null, 2))
  .then(testPromises.bind(null, 3))
  .then(function(value) {
        console.log('all promises resolved');
  }, function(reason) {
        console.log('some promise was rejected');
  });

If you’re doing this a lot, you could make a curry wrapper for testPromises() that would return another function. This is essentially what .bind() above was doing, but the syntax is a little prettier to use once you’ve declared your wrapper function.

function tp(arg) {
    return function() {
        return testPromises(arg);
    }
}

Then, because that wrapper returns another function, you could then do:

testPromises(1).then(tp(2)).then(tp(3))
.then(function(value) {
    console.log('all promises resolved');
}, function(reason) {
    console.log('some promise was rejected');
});


Source: stackoverflow