l p
l p

Reputation: 528

ES6 promise execution order

I would expect the output for the following snippet to be 1, 2, 3, 4. But, the actual output order is 1, 4, 3, 2.

self.promiseChain = new Promise(function (resolve, reject) {
  setTimeout(resolve, 4000);
}).then(function () {
  console.log(1);
});

self.promiseChain.then(function () {
  return new Promise(function (resolve, reject) {
    setTimeout(resolve, 3000);
  }).then(function () {
    console.log(2);
  });
});

self.promiseChain.then(function () {
  return new Promise(function (resolve, reject) {
    setTimeout(resolve, 2000);
  }).then(function () {
    console.log(3);
  });
});
self.promiseChain.then(function () {
  return new Promise(function (resolve, reject) {
    setTimeout(resolve, 200);
  }).then(function () {
    console.log(4);
  });
});

http://www.es6fiddle.net/imu5bhoj/

Everything I've read about promises indicates it should be possible to get the desired order in a 'flat' chain like this. Apparently I'm missing some detail? Could someone help point me in the right direction?

Here's a fiddle (http://www.es6fiddle.net/imu6vh1o/) for how to do it in a non-flat way, but it's harder to reason about and makes sequential chaining awkward.

I've searched similar issues on stack overflow but none of them answer the question generically using a straightforward example (that I could find).

Upvotes: 3

Views: 2633

Answers (1)

jfriend00
jfriend00

Reputation: 707228

You just attached three .then() handlers to the exact same self.promiseChain promise. This is branching, not chaining. With promises, those are very different behaviors. Those are three handlers that will all be called one immediately after the other (without waiting for results) when self.promiseChain is resolved. So, the resulting three async operations will run in parallel and finish whenever they finish, thus the results you see.

If you wanted these four operations to be sequenced, then you have to actually chain them one to another, not all chained onto the same promise. Remember .then() returns a new promise and it is that returned promise that you want to chain to in order to sequence things.

You are doing this:

var p = somePromise();

p.then(fn1);
p.then(fn2);
p.then(fn3);

This will trigger fn1, fn2 and fn3 at basically the same time and fn2 will not wait for the fn1 promise to resolve.

If you want to sequence the operations, then you want this type of logic:

var p = somePromise();

p.then(fn1).then(fn2).then(fn3);

This will not execute fn2 until the fn1 promise is done and will not execute fn3 until the fn2 promise is done - thus sequencing the async operations.

Here's how it would be if they were actually sequenced one after another. You can actually run this snippet (but have patience because it takes 10 seconds to run):

var self = {};

self.promiseChain = new Promise(function (resolve, reject) {
  setTimeout(resolve, 4000);
}).then(function () {
  log(1);
});

var p = self.promiseChain.then(function () {
  return new Promise(function (resolve, reject) {
    setTimeout(resolve, 3000);
  }).then(function () {
    log(2);
  });
});

p = p.then(function () {
  return new Promise(function (resolve, reject) {
    setTimeout(resolve, 2000);
  }).then(function () {
    log(3);
  });
});
p = p.then(function () {
  return new Promise(function (resolve, reject) {
    setTimeout(resolve, 200);
  }).then(function () {
    log(4);
  });
});

p.then(function() {
   // last promise is done now
   log("all done");
});

function log(x) {
  var div = document.createElement("div");
  div.innerHTML = x;
  document.body.appendChild(div);
}

See these other similar answers:

Execute native js promise in series

Understanding javascript promises; stacks and chaining

Is there a difference between promise.then.then vs promise.then; promise.then

Upvotes: 10

Related Questions