事件循环
异步程序是逐片段执行的。 每个片段可能会启动一些操作,并调度代码在操作完成或失败时执行。 在这些片段之间,该程序处于空闲状态,等待下一个动作。
所以回调函数不会直接被调度它们的代码调用。 如果我从一个函数中调用setTimeout
,那么在调用回调函数时该函数已经返回。 当回调返回时,控制权不会回到调度它的函数。
异步行为发生在它自己的空函数调用堆栈上。 这是没有Promise
的情况下,在异步代码之间管理异常很难的原因之一。 由于每个回调函数都是以几乎为空的堆栈开始,因此当它们抛出一个异常时,你的catch
处理程序不会在堆栈中。
try {
setTimeout(() => {
throw new Error("Woosh");
}, 20);
} catch (_) {
// This will not run
console.log("Caught!");
}
无论事件发生多么紧密(例如超时或传入请求),JavaScript 环境一次只能运行一个程序。 你可以把它看作在程序周围运行一个大循环,称为事件循环。 当没有什么可以做的时候,那个循环就会停止。 但随着事件来临,它们被添加到队列中,并且它们的代码被逐个执行。 由于没有两件事同时运行,运行缓慢的代码可能会延迟其他事件的处理。
这个例子设置了一个超时,但是之后占用时间,直到超时的预定时间点,导致超时延迟。
let start = Date.now();
setTimeout(() => {
console.log("Timeout ran at", Date.now() - start);
}, 20);
while (Date.now() < start + 50) {}
console.log("Wasted time until", Date.now() - start);
// → Wasted time until 50
// → Timeout ran at 55
Promise
总是作为新事件来解析或拒绝。 即使已经解析了Promise
,等待它会导致你的回调在当前脚本完成后运行,而不是立即执行。
Promise.resolve("Done").then(console.log);
console.log("Me first!");
// → Me first!
// → Done
在后面的章节中,我们将看到在事件循环中运行的,各种其他类型的事件。
当前内容版权归 wizardforcel 或其关联方所有,如需对内容或内容相关联开源项目进行关注与资助,请访问 wizardforcel .