繁体   English   中英

Javascript,承诺和 setTimeout

[英]Javascript, Promises and setTimeout

我一直在玩 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)

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

正如预期的那样。

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

将要解决的 promise setTimeout 计时器值从 10ms 更改为 1ms

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 

对此有何解释?

并发 model 和事件循环

  • setTimeout在其计时器到期后不会立即运行
  • 零延迟实际上并不意味着回调将在零毫秒后触发。 以 0(零)毫秒的延迟调用setTimeout不会在给定时间间隔后执行回调 function。 基本上,即使您为 setTimeout 指定了特定的时间限制, setTimeout也需要等待排队消息的所有代码完成。

如果我们设置 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)
});

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 ❌

结论

如果你想要一个正确的行为,值得摆脱零延迟。

我将使用下面的例子来解释:

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)]

因此这将打印

1 ms timeout
0 ms timeout

上面的理解:调用 setTimeouts 是同步的(即使它的回调放在异步队列中),即我们调用 setTimeout() 并移动到下一条语句 - 这个同步动作本身可能需要 1ms。

换句话说,1ms 太短了,所以当 JS 引擎看到第二个异步语句时,第一个已经在队列中花费了 1ms。

我还建议您尝试以下方法

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)]

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

另外值得一试的是:

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

多次尝试以上,你会发现有时控制台日志会有不同的时间戳。 粗略地说,你可以说console.log()Date.now()需要 0.5 毫秒。 只是调用/执行同步内容的时间。

Chrome 的硬编码最小超时为 1ms

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

因此,对于 Chrome,您的所有setTimeout( fn, 0 )都将转换为setTimeout( fn, 1 ) ,因此计划在您安排的第一个之后触发(请记住 Promise 构造函数是同步调用的)。

所以我们实际上可以简化你的例子

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

在 Chrome中, 1ms延迟总是首先触发,这与常识相反,因为在内部它实际上是:

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

如果您将其设置为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