Javascript, Promises and setTimeout

╄→гoц情女王★ 提交于 2020-05-13 07:34:52

问题


I have been playing with Promises, but I am having trouble understanding what is happening with the following code:

const promise = new Promise((resolve, reject) => {
  console.log('Promise started - Async code started')
  setTimeout(() => {
    resolve('Success')
  }, 10)
})

setTimeout(() => {
  console.log('Promise log inside first setTimeout')
}, 0)

promise.then(res => {
  console.log('Promise log after fulfilled')
})

console.log('Promise made - Sync code terminated')

setTimeout(() => {
  console.log('Promise log inside second setTimeout')
}, 0)

The output is:


Promise started - Async code started 
Promise made - Sync code terminated 
Promise log inside first setTimeout 
Promise log inside second setTimeout 
Promise log after fulfilled

It is as expected.

But let check the output of the below code:

const promise = new Promise((resolve, reject) => {
  console.log('Promise started - Async code started')
  setTimeout(() => {
    resolve('Success')
  }, 1)
})

setTimeout(() => {
  console.log('Promise log inside first setTimeout')
}, 0)

promise.then(res => {
  console.log('Promise log after fulfilled')
})

console.log('Promise made - Sync code terminated')
setTimeout(() => {
  console.log('Promise log inside second setTimeout')
}, 0)

Changed the to be resolved promise setTimeout timer value from 10ms to 1ms

The output is:

Promise started - Async code started 
Promise made - Sync code terminated 
Promise log after fulfilled 
Promise log inside first setTimeout 
Promise log inside second setTimeout 

Any explanation for this?


回答1:


I will use the following example to explain:

setTimeout(() => {
  console.log('1 ms timeout');
}, 1);                            // Moved to async queue at time = T0
setTimeout(() => {
  console.log('0 ms timeout')
}, 0);                            // Moved to async queue after 1 ms that synchronous call to setTimeout takes i.e. at T1
                                  // So at T1, queue will be [("1ms timeout", 0), ("0ms timeout", 0)]

Hence this will print

1 ms timeout
0 ms timeout

Understanding of above: Calling setTimeouts is synchronous (even though its callback is put in async queue), i.e. we call setTimeout() and move to next statement - this synchronous action itself may take 1ms.

In other words, 1ms is too low a time so by the time JS engine sees the 2nd async statement, the first one has already spent 1ms in the queue.

I also suggest you try out the following

setTimeout(() => {
  console.log("First");
}, 2);                      // queue at T0 = [("First", 2)]

const forLoopLimit = 100;
for (var i = 0; i < forLoopLimit; i++){
    console.log(i * 10000);
}                           // Assume that it takes about 3 milliseconds
                            // queue at T3 = [("First", 0)]
setTimeout(() => {
  console.log("Second");
}, 0);                      // Assume it takes 0 milliseconds.
                            // queue at T4 = [("First", 0), ("Second", 0)]

This will print First before Second even though the former had 2ms timeout compared to the latter having 0ms. Now change forLoopLimit to 1 or even 10, you'll see that the synchronous task doesn't take 3 milliseconds now, and Second is printed before First

Also worth trying is:

console.log(Date.now());
console.log(Date.now());

Try above multiple times and you'll see that sometimes console logs will have different timestamps. Roughly, you can say console.log() and Date.now() take 0.5ms. It's nothing but the time to call / execute synchronous stuff.




回答2:


From Concurrency model and the event loop

  • setTimeout does not run immediately after its timer expires
  • Zero delay doesn't actually mean the call back will fire-off after zero milliseconds. Calling setTimeout with a delay of 0 (zero) milliseconds doesn't execute the callback function after the given interval. Basically, the setTimeout needs to wait for all the code for queued messages to complete even though you specified a particular time limit for your setTimeout.

What happen if we set 2 and 1 milliseconds:

const promise = new Promise((resolve, reject) => {
  console.log('Promise started - Async code started')
  setTimeout(() => {
    resolve('Success')
  }, 2)
})

console.log('Promise log inside first setTimeout 1')
setTimeout(() => {
  console.log('Promise log inside first setTimeout 2')
}, 1)

promise.then(res => {
  console.log('Promise log after fulfilled ❌')

})

console.log('Promise log inside second setTimeout 1')
setTimeout(() => {
  console.log('Promise log inside second setTimeout 2')
}, 1)
});

The output always will be:

Promise started - Async code started
Promise log inside first setTimeout 1
Promise log inside second setTimeout 1
Promise log inside first setTimeout 2
Promise log inside second setTimeout 2
Promise log after fulfilled ❌

Conclusion

If you want a proper behavior, worth to get rid of Zero delays.



来源:https://stackoverflow.com/questions/61608974/javascript-promises-and-settimeout

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!