-3

Program - 1

new Promise(resolve => {
        resolve('1');
        Promise.resolve().then(() => console.log('2'));
    }).then(data => {
        console.log(data);
    }); // output 2 1

Program -2

new Promise(resolve => {
        Promise.resolve().then(() => {
            resolve('1');
            Promise.resolve().then(() => console.log('2'));
        });
    }).then(data => {
        console.log(data);
    }); // output 1 2

I am really confused with the output of both the programs. Can anyone please tell me how the execution thread works here?

Rohit Goyal
  • 212
  • 1
  • 2
  • 8
  • 1
    `I am really confused with the output of both the programs.` And why? What output did you expect and why? – t.niese Mar 29 '20 at 18:01
  • Uh, what you need to understand here is that the two logs are simply unrelated, and you should not expect any particular order between them. If you need one, write a proper promise chain. – Bergi Mar 29 '20 at 18:42
  • The function in `.then(func)` executes after the `Promise` is resolved, passing the resolve argument to it. – StackSlave Mar 30 '20 at 04:47

2 Answers2

0

What is hard to understand is that resolve doesn't work the same way as return. Creating Promises actually creates a new async context. Everything inside the first function of the Promise is executed synchronously. Then the .then() methods are called with the values resolved synchronously or asynchronously.

In the first example, you resolve '1', but then you create a new Promise with Promise.resolve() and you call the .then() right away. Because the new Promise is inside the first one, everything is called synchronously.

new Promise(resolve => {
        resolve('1'); // Will be accessed in the .then() after then end of the callback
        Promise.resolve().then(() => log('2')); // called before the end of the function 
                                               // because the context is not yet created
    }).then(data => {
        log(data);
    }); // output 2 1

The second example is way harder. You actually create a Promise inside the first Promise and call its resolve right away. The order of execution will be:

  • first everything in the initial callbacks
  • After that, create sort of eventListeners from the resolve to the .then
  • When a resolve is called, execute the callback in .then

There is nothing in the initial function of the nested promise. So it goes straight to the then. The .then() calls the initial resolve(). Here, the kind of eventListener is created so we jump to the initial callback. Then we go back and execute the rest of the function.

new Promise(resolve => {
        // Because we call resolve right away, the context of the original promise
        // is created
        Promise.resolve().then(() => {
            resolve('1'); // Calling resolve jumps us to the .then
            Promise.resolve().then(() => log('2')); // We execute after.then
        });
    }).then(data => {
        log(data);
    }); // output 1 2

If you removed the .resolve(), it would work like the first one:

new Promise(resolve => {
        new Promise(() => {
            resolve('1');
            Promise.resolve().then(() => log('2'));
        });
    }).then(data => {
        log(data);
    }); // output 2 1
Alexandre Senges
  • 1,474
  • 1
  • 13
  • 22
  • 2
    "*Because the new Promise is inside the first one, everything is called synchronously.*" - no. the `log('2')` is asynchronous as well. – Bergi Mar 29 '20 at 18:42
  • @Bergi What I mean is that you have not created the context for the first Promise yet because you haven't left the scope of the initial function. What's really happening is that `resolve('1')` doesn't have a listener yet, so there is no associated event in the event loop. `Promise.resolve().then(() => log('2'));` pushes and consumes an event in the loop right away. When leaving the scope of the first function, you create the listener and instantly consume it with the value in the `resolve` – Alexandre Senges Mar 29 '20 at 18:49
  • @Bergi you have to appreciate how hard it is to explain – Alexandre Senges Mar 29 '20 at 18:52
  • "*pushes and consumes an event in the loop right away*" - exactly that's what is not happening. It does schedule the callback immediately, yes, but it does not actually execute it before the `new Promise` executor is left. – Bergi Mar 29 '20 at 18:52
  • Also, "context" doesn't sound like the right term here, probably causing more confusion than necessary. And "*Calling resolve jumps us to the .then*" is just wrong. – Bergi Mar 29 '20 at 18:53
  • @Bergi it would be grest of you can explain this behaviour. I am really struggling with this from past few days snd could not able to come up with a proper reasoning. Thanks in advance. – Rohit Goyal Mar 30 '20 at 03:25
  • @RohitGoyal It's all in the details of the synchronous `new Promise` executor callback and `resolve` or `then` scheduling callbacks when the promise is fulfilled, callbacks that will be executed in the same order they were scheduled in. But all of this is irrelevant, one should never need to reason about scheduling order - if you write a program where you rely on the ordering, be explicit about it and write `Promise.resolve().then(() => log(1)).then(() => log(2))`. – Bergi Mar 30 '20 at 16:15
-1

The difference lies in the context that the event loop is in when resolving the promise (resolve(1)).

There are 2 queues that execute code in JS:

  • microtask Q
  • macrotask Q

JS executes a microtask and then runs all the tasks from the macrotask Q (if any)

When resolving a promise from the executor (Promise(...)) you are running inside the macrotask Q. Code executed from inside a promise callback is executed on the microtask Q.

The difference that matters in this case is that when running from inside the microtask Q if you add other microtask (promise callbacks are microtasks) they get added to the current Q and get processed during this queue run.

This is what happens in case no 2, you are resolving the promise from inside a microtask Q, this ends up resolving the top level Promise and add the .then(data => { log(data); }); to the current microtask Q. So the callback will get executed in this run. On the other hand, the handlers of the nested promise Promise.resolve() is not executed now, as handlers are always called async.

Note: it is possible to add microtasks ad infinitum from inside the microtask Q, blocking the execution.

Radu Diță
  • 13,476
  • 2
  • 30
  • 34