Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript Promise then() ordering

I'm still learning JavaScript Promises, and I came across a behavior I don't understand.

var o = $("#output");
var w = function(s) {
    o.append(s + "<br />");
}

var p = Promise.resolve().then(function() {
    w(0);
}).then(function() {
    w(1);
});

p.then(function() {
    w(2);
    return new Promise(function(r) {
        w(3);
        r();
    }).then(function() {
        w(4);
    });
}).then(function() {
    w(5);
});

p.then(function() {
    w(6);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>

I would expect these statements to run in order--that is, the that output would be

0
1
2
3
4
5
6

Instead, the output is

0
1
2
3
6
4
5

Even removing the inner Promise gives, what seems to me to be, contradicting results. 1 is output before 2, but 6 is output before 5.

Can someone explain this to me?

Something I have noticed is that reassigning p each time gives us the order I would expect.

like image 396
dx_over_dt Avatar asked Mar 17 '15 23:03

dx_over_dt


People also ask

Does promise all resolve in order?

While you'll get the resolution in the same order, there's no guarantee about when the promises are acted on. In other words, Promise. all can't be used to run an array of promises in order, one after the other.

What is promise resolve () then?

resolve() The Promise. resolve() method "resolves" a given value to a Promise . If the value is a promise, that promise is returned; if the value is a thenable, Promise. resolve() will call the then() method with two callbacks it prepared; otherwise the returned promise will be fulfilled with the value.

How do you return from promise then?

Return value returns a value, the promise returned by then gets resolved with the returned value as its value. doesn't return anything, the promise returned by then gets resolved with an undefined value. throws an error, the promise returned by then gets rejected with the thrown error as its value.

How do you resolve Promises sequentially?

The essence of this function is to use reduce starting with an initial value of Promise. resolve([]) , or a promise containing an empty array. This promise will then be passed into the reduce method as promise . This is the key to chaining each promise together sequentially.


3 Answers

The reason you see 6 early is because you didn't chain, you branched.

When you call p.then().then().then(), you've got a chain of promises that must execute in the correct order.
However, if you call p.then().then(); p.then(), you've got 2 promises attached to p - essentially creating a branch, and the 2nd branch will execute along with the first.

You can fix this by ensuring you chain them together p = p.then().then(); p.then();

FYI, you almost NEVER want to branch, unless you bring them back together (eg. Promise.all), or are intentionally creating a "fire and forget" branch.

like image 181
Scott Rippey Avatar answered Oct 18 '22 14:10

Scott Rippey


What does r() do?

The ordering is indeterminate because you're thenning on the same promise -> this specifically refers to the second and third chain.

If you were doing the following, then order can be guaranteed:

var p = Promise.resolve().then(function() {
    w(0);
}).then(function() {
    w(1);
});

// Key difference, continuing the promise chain "correctly".
p = p.then(function() {
    w(2);
    return new Promise(function(r) {
        w(3);
        r();
    }).then(function() {
        w(4);
    });
}).then(function() {
  w(5);
});

p.then(function() {
  w(6);
});
like image 36
irysius Avatar answered Oct 18 '22 14:10

irysius


Let's give every promise and function in your example a name for clarity:

var pz = Promise.resolve();
function f0() { w(0); }
var p0 = pz.then(f0);
function f1() { w(1); }
var p1 = p0.then(f1);  // p1 is 'p' in your example

function f2() {
    w(2);
    function f3(resolve_p3) {
        w(3);
        resolve_p3();
    }
    var p3 = new Promise(f3);
    function f4() { w(4); }
    var p4 = p3.then(f4);
    return p4;
}
var p2 = p1.then(f2);
function f5() { w(5); }
var p5 = p2.then(f5);

function f6() { w(6); }
var p6 = p1.then(f6);

Let's see what happens step by step. First the top level execution:

  • pz is fulfilled, hence pz.then(f0) immediately queues f0 for execution, the result of which will resolve p0.
  • f1 is scheduled to be queued once p0 is fulfilled, and the result thereof will resolve p1.
  • f2 is scheduled to be queued once p1 is fulfilled, and the result thereof will resolve p2.
  • f5 is scheduled to be queued once p2 is fulfilled, and the result thereof will resolve p5.
  • f6 is scheduled to be queued once p1 is fulfilled, and the result thereof will resolve p6.

Then the queued jobs (initially just f0) will be run:

  • f0 is executed: prints "0". p0 becomes fulfilled, hence f1 is added to the queue.
  • f1 is executed: prints "1". p1 becomes fulfilled, hence f2 and f6 are added to the queue (in that order). This is the crucial bit, since it means f6 will be executed before any jobs queued later.
  • f2 is executed: prints "2".
  • (inside f2): new Promise calls f3, which prints "3" and fulfills p3.
  • (inside f2): since p3 is already fulfilled, f4 is added to the queue, the result of which will resolve p4.
  • f2 finally resolves p2 to p4, which means that once p4 becomes fulfilled, so will p2.
  • f6 is executed: prints "6". p6 becomes fulfilled.
  • f4 is executed: prints "4". p4 becomes fulfilled. p2 becomes fulfilled, hence f5 is added to the queue.
  • f5 is executed: prints "5". p5 becomes fulfilled.
like image 33
Matthijs Avatar answered Oct 18 '22 16:10

Matthijs