Promise 内的 setTimeout 与 setTimeout

问题描述 投票:0回答:2

我知道

setTimeout()
中的回调函数将等到附加到它的计时器到期并将其推入回调队列。另一方面,一个承诺,一旦完成或被拒绝,它的回调就会被推送到一个具有更高优先级的微任务队列中。

我的问题是哪个更快:承诺中的

setTimeout
或简单的
setTimeout
。如果前者不再被放入微任务队列,为什么
setTimeout
单独先运行而不是相反?

setTimeout(() => {
  console.log('timeout');
}, 1000);

let promise = new Promise(function(resolve, reject) {
  // This is run automatically, let's run resolve after 1 second
  setTimeout(() => resolve('promise!'), 1000);
});

promise.then(
  (result) => console.log(result),
  (err) => console.log(error)
);

// output: timeout -> promise || and not || promise -> timeout

现在假设我忘记了 1 秒的延迟,现在 promise 总是首先出现,因为在微任务队列中调度的回调具有更高的优先级

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

let promise = new Promise(function(resolve, reject) {
  // This is run automatically, let's run resolve after 1 second
  // setTimeout(() => resolve('promise!'), 1000);
  resolve('');
});

promise.then(() => {
  console.log('promise');
});

javascript promise callback
2个回答
1
投票

setTimeout
的实现方式是在给定的最小延迟后执行,一旦浏览器的线程可以自由执行它。因此,举例来说,如果您为 delay 参数指定一个值 0,并且您认为它会“立即”执行,那么它不会。更准确地说,它将在下一个事件循环中运行(这是事件循环的一部分 - 负责执行代码的并发模型)。

我们以延迟值为0为例

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

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve('promise!'), 0);
});

promise.then(
  (result) => console.log(result),
  (err) => console.log(error)
);

setTimeout
总是首先记录它的结果,因为它肯定会在下一个事件周期中执行。

另一方面,promise 中的

setTimeout
将有 2 个事件周期,直到控制台日志执行(一个用于 promise 解析,另一个用于 setTimeout 函数中的回调)。

请阅读延迟时间超过指定时间的原因 - https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified

关于 JS 事件循环的更多信息 - https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop


0
投票

关键要点是

每次任务退出时,事件循环检查任务是否将控制权返回给其他 JavaScript 代码。如果没有,它将运行微任务队列中的所有微任务。

在下面的第一个示例中,一旦执行主程序的任务退出,就没有任何微任务要处理,因为由于第二次 setTimeout,promise 尚未解决。然后处理任务队列,因此首先记录“超时”。之后,处理第二个 setTimeout 并解决承诺,然后在控制台打印解决结果。

setTimeout(() => {
  console.log('timeout');
}, 1000);

let promise = new Promise(function(resolve, reject) {
  // This is run automatically, let's run resolve after 1 second
  setTimeout(() => resolve('promise!'), 1000);
});

promise.then(
  (result) => console.log(result),
  (err) => console.log(error)
);

// output: timeout -> promise || and not || promise -> timeout

而在您的第二个示例中,一旦执行主程序的任务退出,承诺就会得到解决,因此首先处理微任务队列并记录“承诺”。然后处理任务队列并记录“超时”。

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

let promise = new Promise(function(resolve, reject) {
  // This is run automatically, let's run resolve after 1 second
  // setTimeout(() => resolve('promise!'), 1000);
  resolve('');
});

promise.then(() => {
  console.log('promise');
});

我强烈推荐阅读这篇文章,这有助于我深入了解任务队列和微任务队列的工作原理。

© www.soinside.com 2019 - 2024. All rights reserved.