amaack
amaack

Reputation: 96

Why are Javascript promises resolving out of order?

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

Upvotes: 3

Views: 1707

Answers (3)

jfriend00
jfriend00

Reputation: 707228

.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');
});

Upvotes: 9

Felipe Sabino
Felipe Sabino

Reputation: 18215

Well,

handling promise 1
handling promise 2
handling promise 3

occurs first because these calls are independent from the Promise workflow, they are executed right when you create each Promise object, because to do that you explicit execute each function like testPromises(1) and so on.

But the problem is that your call to console.log('all promises resolved'); depends only on testPromises(1) being resolved, all your other promises are detached.

In order do sync them, each .then call have to return a Promise, and you can do it by using a function and returning the Promises testPromises function already returns.

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

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');
});

Upvotes: 0

jakub.g
jakub.g

Reputation: 41238

This is the code to get the output you want:

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');
});

Promises are resolved asynchronously, but created synchronously.

testPromises(1).then(testPromises(2)).then(testPromises(3))

is more or less like doing

p1 = testPromises(1)
p2 = testPromises(2)
p3 = testPromises(3)

p1.then(p2).then(p3)

Since new Promise is synchronous, the first 3 lines you'll see will be

handling promise 1
handling promise 2
handling promise 3

Upvotes: 3

Related Questions