-1
function main() {
  console.log("S-1");

  setTimeout(() => {
    console.log("setTimeout");
  }, 0);

  new Promise((resolve, reject) => {
    for (let i = 0; i < 10000000000; i++) {}
    resolve("Promise");
  }).then((res) => console.log(res));

  console.log("S-2");
}

// Invoke function
main();

When I run the main() function, the output I get is:

'S-1'
'S-2'
'Promise'
'setTimeout'

After 'S-1' is logged, there is a big time gap, and then after few seconds, the rest gets logged, at once. Shouldn't the time gap occur after logging 'S-2'?

RedPotato
  • 37
  • 5
  • No, the function you pass to the Promise constructor is guaranteed to run synchronous. You could as well have written `for (let i = 0; i < 10000000000; i++) {} Promise.resolve("Promise").then((res) => console.log(res))` – Thomas Jun 11 '22 at 21:25
  • 1
    JavaScript is single-threaded. – Ram Jun 11 '22 at 21:29

2 Answers2

3

The function inside the Promise constructor runs synchronously, on the same (and only) main thread as the rest of the script. Constructing a Promise doesn't result in a separate thread being created.

If you do some heavy processing immediately inside a Promise constructor, that heavy processing will have to complete before control flow is yielded back to the outside of the Promise constructor. In this case, it means that the many iterations of the loop must finish before:

  • the constructor finishes completely
  • The constructor resolves to a Promise
  • That Promise gets a .then handler attached to it
  • Finally, the console.log("S-2"); line runs

To get the output you're desiring or expecting without moving the logs around, you'd have to offload the expensive code to a different environment, such as to a worker or a server (using a network request).

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
1

As @CertainPerformance had said, Javascript can only handle one call-stack. It needs to finish this thread once and for all. For promises and timeouts, it uses the job and task queues. The event loop prioritizes the job queue and then the task queue.

Lets try to understand what you wrote in terms of call-stack and queues.

function main() {
// 1. execute straight forward 
  console.log("S-1"); 

// 2. sets / schedules a timer, the anonymous function is then stored in the callback 
  setTimeout(() => {
    console.log("setTimeout");
  }, 0);

// 3. create a new Promise, with the constructor acting as executor
// executes immediately, as its designed to set the state of the Promise for the then() to react to.
  new Promise((resolve, reject) => {
    for (let i = 0; i < 10000000000; i++) {}
// 3.5 the resolve signals that the execution is finished
// either returns the promise or passes through the then()
    resolve("Promise");

// 4. after the promise execution has been resolved, 
// then() creates another anonymous function, and stores in the callback.
// at this point there are 2 entries in the callback
  }).then((res) => console.log(res));

// 5. once the above Promise executor has been executed, execute this
  console.log("S-2");

// By now, since both the timer and promise execution has been done, 
// the timeout is put into the Task Queue and the Promise then() is  put into the Job Queue.

}

// 0. puts this into the call stack and execute
main();   


// 6. The event loop deques all of the call stack from the job queue and puts it into the main call stack to execute.
// hence, the then() is executed first

// 7. The event loop then deques all of the call stack from the task queue and puts it into the main call stack to execute.
// hence, the timeout function is executed next.

If you want to make it a callback.

function main() {
  console.log("S-1");

  setTimeout(() => {
    console.log("setTimeout");
  }, 0);

  new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("promise");
    }, 1000);
  }).then((res) => console.log(res));

  console.log("S-2");
}

// Invoke function
main();

Reference: https://medium.com/@idineshgarg/let-us-consider-an-example-a58bb1c11f55

isatori
  • 146
  • 1
  • 8
  • It's not quite clear what you mean by "*delegates to another stack*", "*by now, another stack has the then() of the promise and the timeout anonymous function. it will execute that in that order*" and "*since there's no more, execute*", but I think it's painting a wrong picture – Bergi Jun 11 '22 at 23:02
  • Thanks @Bergi. I edited and corrected. – isatori Jun 14 '22 at 04:00
  • 2
    Thanks, that's better! I'd suggest to move steps 6 and 7 from inside the (end of the) `main` definition after the `main()` call, to add an explanation what the `resolve()` line does, and how that affects step 4 (whose comment should be moved down, between the constructor and `.then…`), and to clarify that it's the `then()` call that puts the callback on the job queue (since the promise is already fulfilled) – Bergi Jun 14 '22 at 07:47