简体   繁体   English

Javascript,承诺和 setTimeout

[英]Javascript, Promises and setTimeout

I have been playing with Promises, but I am having trouble understanding what is happening with the following code:我一直在玩 Promises,但我无法理解以下代码发生了什么:

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: output 是:


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:但是让我们检查以下代码的 output:

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将要解决的 promise setTimeout 计时器值从 10ms 更改为 1ms

The output is: output 是:

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?对此有何解释?

From Concurrency model and the event loop并发 model 和事件循环

  • setTimeout does not run immediately after its timer expires setTimeout在其计时器到期后不会立即运行
  • 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.以 0(零)毫秒的延迟调用setTimeout不会在给定时间间隔后执行回调 function。 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.基本上,即使您为 setTimeout 指定了特定的时间限制, setTimeout也需要等待排队消息的所有代码完成。

What happen if we set 2 and 1 milliseconds:如果我们设置 2 和 1 毫秒会发生什么:

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: output 始终为:

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.如果你想要一个正确的行为,值得摆脱零延迟。

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), ie we call setTimeout() and move to next statement - this synchronous action itself may take 1ms.上面的理解:调用 setTimeouts 是同步的(即使它的回调放在异步队列中),即我们调用 setTimeout() 并移动到下一条语句 - 这个同步动作本身可能需要 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.换句话说,1ms 太短了,所以当 JS 引擎看到第二个异步语句时,第一个已经在队列中花费了 1ms。

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.即使前者的超时时间为 2 毫秒,而后者的超时时间为 0 毫秒,这也会在Second之前打印First 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现在将forLoopLimit更改为 1 甚至 10,您会看到同步任务现在不需要 3 毫秒,并且在First之前打印了Second

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.粗略地说,你可以说console.log()Date.now()需要 0.5 毫秒。 It's nothing but the time to call / execute synchronous stuff.只是调用/执行同步内容的时间。

Chrome has an hardcoded minimum timeout of 1ms . Chrome 的硬编码最小超时为 1ms

  base::TimeDelta interval_milliseconds =
      std::max(base::TimeDelta::FromMilliseconds(1), interval);

So for Chrome, all your setTimeout( fn, 0 ) are converted to setTimeout( fn, 1 ) , and thus scheduled to fire after the first one you did schedule (remember Promise constructors are called synchronously).因此,对于 Chrome,您的所有setTimeout( fn, 0 )都将转换为setTimeout( fn, 1 ) ,因此计划在您安排的第一个之后触发(请记住 Promise 构造函数是同步调用的)。

So we could actually simplify your example with所以我们实际上可以简化你的例子

 setTimeout( () => console.log( '1ms delay' ), 1 ); setTimeout( () => console.log( '0ms delay' ), 0 );

and in Chrome , the 1ms delay will always fire first, contrary to the common sense because internally it will actually be:在 Chrome中, 1ms延迟总是首先触发,这与常识相反,因为在内部它实际上是:

 setTimeout( () => console.log( '1ms delay' ), Math.max(1, 1) ); setTimeout( () => console.log( '0ms delay' ), Math.max(1, 0) );

If you had it set to 1 and 2 instead of 0 and 1 , your expectation would be fulfilled.如果您将其设置为12而不是01 ,您的期望就会得到满足。

 const promise = new Promise((resolve, reject) => { console.log('Promise started - Async code started') setTimeout(() => { resolve('Success') }, 2) }) setTimeout(() => { console.log('Promise log inside first setTimeout') }, 1) promise.then(res => { console.log('Promise log after fulfilled') }) console.log('Promise made - Sync code terminated') setTimeout(() => { console.log('Promise log inside second setTimeout') }, 1)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM