0

I'm confused about the execution of these two different functions, I was expecting them to resolve in the same order, but apparently, they do not.

    // given this function:
let promiseDelay = () => new Promise( resolve => { setTimeout( () => { console.log( '========' ); resolve(); }, 1000 ) } );

// execution 1:
const promise1 = Promise.resolve( {} ).then( promiseDelay ).then( () => console.log( 'Job done!' ) ).then( promiseDelay ).then( () => console.log( 'Job done!' ) );

// given this function:
const promiseDelay = () => new Promise( resolve => { setTimeout( () => { console.log( '========' ); resolve(); }, 1000 ) } );

// execution 2:
const promise = Promise.resolve( {} );
promise.then( promiseDelay );
promise.then( () => console.log( 'Job done!' ) );
promise.then( promiseDelay );
promise.then( () => console.log( 'Job done!' ) );
felipeclopes
  • 4,010
  • 2
  • 25
  • 35
  • Note in your second one you are not chaining the `then()` returns – Patrick Evans Sep 05 '18 at 16:57
  • Execution 1 uses callbacks after each finishes it goes onto the next, while execution 2 executes them all in order without waiting for the current one to finish before moving onto the next. Execution 1 will ensure they resolve in the order you want, execution 2 will be faster. – Ajaypayne Sep 05 '18 at 16:57
  • Re your [tag:concurrency] tag: Please note that nothing about promises makes that JavaScript code *concurrent* in any of the popular environments (browsers, Node.js). Just asynchronous. (And even then, all the *promise* does is guarantee that the `then` callback will be called asynchronously; other than that, promises don't make synchronous code asynchronous.) – T.J. Crowder Sep 05 '18 at 17:02
  • Multiple `.then()` handlers on the same promise are very much like multiple event listeners to the same event on an `EventEmitter`. They get called one after another in rapid succession, but the outcome of one has nothing to do with the others. To chain, you need `p.then().then().then()`, not `p.then(); p.then(); p.then();` – jfriend00 Sep 06 '18 at 01:04
  • In addition, you only pass a function reference to `.then()`. You don't pass promises to a `.then()` handler. That function reference "may" return a promise when it is executed, but you still must pass the function reference so that the promise infrastructure can call the function sometime later and then and only then examine its return value to decide what to do next. – jfriend00 Sep 06 '18 at 01:07
  • Related answer that describes branching vs. chaining as in `p.then(); p.then();` vs. `p.then().then()`: [Understanding Javascript promises stacks and chaining](https://stackoverflow.com/questions/29853578/understanding-javascript-promises-stacks-and-chaining/29854205#29854205) – jfriend00 Sep 06 '18 at 01:10
  • Other related answers: [Is there a difference between promise.then().then() vs promise.then(); promise.then()](https://stackoverflow.com/questions/32216619/is-there-a-difference-between-promise-then-then-vs-promise-then-promise-then/32216660#32216660) and [multiple `.then()`s on single angularjs promise — all use _original_ data](https://stackoverflow.com/questions/25345520/multiple-thens-on-single-angularjs-promise-all-use-original-data/25346201#25346201). – jfriend00 Sep 06 '18 at 01:14
  • And, on the issue of passing a promise to `.then()`: [Javascript promise not waiting for previous promise to execute](https://stackoverflow.com/questions/49207086/javascript-promise-chains-not-waiting-for-previous-promise-to-execute/49207123#49207123) and [Promise resolve inside then does not pass its resolved value](https://stackoverflow.com/questions/42539369/promise-resolve-inside-then-method-does-not-pass-its-resolved-value/42541019#42541019). – jfriend00 Sep 06 '18 at 01:16

3 Answers3

3

You aren't actually chaining promises here, you are just attaching multiple functions to the Promise that all get executed at the same time.

To chain promises, write the next .then()s after each other:

// given this function:
promiseDelay = () => new Promise( resolve => { setTimeout( () => { console.log( '========' ); resolve(); }, 1000 ) } );

// execution 1:
promise = Promise.resolve( {} ).then( promiseDelay ).then( () => console.log( 'Job done!' ) ).then( promiseDelay ).then( () => console.log( 'Job done!' ) );

// execution 2:
promise = Promise.resolve( {} );
promise.then( promiseDelay )
    .then( () => console.log( 'Job done!' ) )
    .then( promiseDelay )
    .then( () => console.log( 'Job done!' ) );
Luca Kiebel
  • 9,790
  • 7
  • 29
  • 44
2

One of the key aspects of promises is that then and catch return new promises. The promise they return is either fulfilled or rejected depending on what their callback function does:

  • If it returns a simple value, the promise is fulfilled with that value
  • If it throws a value (throw), the promise is rejected with that value
  • If it returns a promise (or more generally, a thenable), the promise is resolved to that other promise: It waits for that promise to settle, and settles the same way

That's why chaining does what you expect, but your second example (which doesn't use chaning), doesn't. All four of those then handlers in the second example are independent of one another, whereas in the first example, they wait for one another.

Here's an example illustrating how then returns a new promise, see inline comments:

const delay = (ms, value) =>
    new Promise(resolve => setTimeout(resolve, ms, value));

const p1 = delay(100, "p1 result");

const p2 = p1.then(result => {
    console.log("p2 handler got: " + result);
    return delay(500, "p2 result");
});
// This shows false: `then` returns a new promise
console.log(p1 === p2);

const p3 = p1.then(result => {
    console.log("p3 handler got: " + result);
    return "p3 result"; // no delay
});
// Also shows false: `then` returns a new promise *each time*
console.log(p2 === p3);

// Notice that p4's handler doesn't get called
// until the promise returned in p3's handler settles...
const p4 = p2.then(result => {
    console.log("p4 handler got: " + result);
});
// ...but p5's gets called earlier, because p3's
// handler returns a simple value, which just
// gets wrapped in a fulfilled promise
const p5 = p3.then(result => {
    console.log("p5 handler got: " + result);
});
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
0

The key aspect of the first example is that .then( promiseDelay ) is returning a promise that delay the execution for one second, so the next promise just executes after this is resolved, as each .then receives the result of the .then before as parameter, thus creating a chain.

In the second example, each line is returning a new promise, but as there is no await, each line is evaluated sequentially, and the promises just became delayed executions, so the ones with console.log will finish execution first than those with a one second delay.

szanata
  • 2,402
  • 17
  • 17