我在以下代码中观察到了这一点:
setTimeout(function(){console.log('setTimeout')});
Promise.resolve(1).then(function(){console.log('promise resolve')})
无论我执行多少次,promise 回调总是在 setTimeout 之前记录。
我的理解是,这两个回调都计划在下一个滴答声中执行,我真的不明白发生了什么使得Promise始终优先于超时。
我在以下代码中观察到了这一点:
setTimeout(function(){console.log('setTimeout')});
Promise.resolve(1).then(function(){console.log('promise resolve')})
无论我执行多少次,promise 回调总是在 setTimeout 之前记录。
我的理解是,这两个回调都计划在下一个滴答声中执行,我真的不明白发生了什么使得Promise始终优先于超时。
Promise.resolve 调度一个微任务, setTimeout 调度一个宏任务。并且在运行下一个宏任务之前执行微任务。
简短回答Promise比事件循环堆栈中的 setTimeout 回调函数具有更好的优先级(或我如何理解它)。
长答案看这个视频。很有帮助。希望这可以帮助。
https://www.youtube.com/watch?v=8aGhZQkoFbQ
感谢@MickJuice 为事件循环提供新的和更新的视频。
setTimeout()
拥有4ms的最小延迟,所以即使你没有指定代码中的延迟超时仍将至少4毫秒延迟。您的Promise.then()
在此期间被调用。
超时和Promise都用于以异步方式执行代码,但具有不同的特征和目的:
setTimeout - 按特定的持续时间延迟函数的执行。- 不阻塞其余的代码执行(异步行为) - 他们创建 Macrotask(浏览器内部操作)
Promises - 它们是允许异步执行代码的包装器(例如:ajax 调用)。(不依赖于特定的持续时间) - 它们对于链接不同的异步调用特别有用。- 在使用 await 运算符的情况下,不会阻止其余的代码执行(异步行为)。- 他们创建微任务(浏览器内部操作),其优先于宏任务。
推荐
当您希望将函数执行延迟某个特定的持续时间而不阻止进程中的其余代码执行时,请使用setTimeout
使用Promises:当你想执行一些异步代码并避免“回调地狱”时(是的,因为你可以在没有 Promises 的情况下进行异步 ajax 调用,但语法不太清晰,更容易出错)
这与Web Spec 中定义的事件循环有关。浏览器有多个任务队列用于多种类型的任务(例如通过创建的定时器任务),以及一个微任务队列(Promise 结算被推送到的地方)。每当浏览器完成执行任务时,它会清空微任务队列并执行其中的所有任务,然后再继续执行另一个任务队列中的任务。setTimeout
因此在代码执行后(这是一个任务),Promise 结算在微任务队列内,而定时器任务可能已经在任务队列内¹。微任务队列被清空,Promise 被解析。然后在计时器任务运行的某个时候。
¹ 浏览器可能会选择稍微增加超时时间,他们确实这样做了。在大多数浏览器中,0 毫秒后永远不会运行超时。