问题
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, thesetTimeout
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