这段代码片段的执行顺序问题

12

这些天我一直在阅读有关JavaScript promise的教程。

以下是其中一个例子,用于解释宏任务队列(即事件循环)和微任务队列。

let promise = Promise.reject(new Error("Promise Failed!"));
promise.catch(err => alert('caught'));

// no error, all quiet
window.addEventListener('unhandledrejection', event => alert(event.reason));

它说,因为promise.catch捕获了错误,所以最后一行事件处理程序没有运行。我可以理解这个。但是他稍微修改了这个例子。

let promise = Promise.reject(new Error("Promise Failed!"));
setTimeout(() => promise.catch(err => alert('caught')));

// Error: Promise Failed!
window.addEventListener('unhandledrejection', event => alert(event.reason));

这次,他说事件处理程序将会首先运行并捕获错误,之后 promise.catch 最终也会捕获错误。

我不明白第二个例子的问题在哪里,为什么事件处理程序在 promise.catch 之前运行?

我的理解是:

  1. 第一行,我们遇到一个 Promise,并把它放在微任务队列中。
  2. 第二行,我们有一个 setTimeout ,我们把它放在宏任务队列中。
  3. 第三行,我们有一个事件处理程序,我们把处理程序放在宏任务队列中等待被触发。

因为微任务的优先级高于宏任务,所以我们首先运行 Promise。之后,我们从宏任务队列中出队第一个任务,即 setTimeout。所以根据我的理解,错误应该由 setTimeout 内部的函数捕获。

请纠正我。


返回:

这里需要更正的是,在第二个例子中,事件处理程序确实在 promise.catch 之前运行,因为事件处理程序是宏任务而不是微任务。因此,事件处理程序将在 Promise 执行完之后立即执行,但在 setTimeout 函数之前。


看一下 https://www.youtube.com/watch?v=8aGhZQkoFbQ 这个关于事件循环的演示帮了我很多! - fwoelffel
1个回答

3
你对第三步的理解是错误的。处理程序将被同步添加。然后微任务队列被运行,Promise 被拒绝。由于尚未添加任何 .catch 处理程序,因此会引发未处理的拒绝。
我认为你混淆了“添加回调函数时”和“执行回调函数时”的概念。考虑以下情况:
  (new Promise).then(function callback() { });

回调函数将同步添加,但由于承诺永远不会解决,因此它永远不会被调用。在这种情况下:
  Promise.resolve().then(function callback() { });

回调函数会同步地被再次添加,但是Promise的解决在微任务中执行,因此回调函数将会稍后一个 tick被执行。


如果像你所说的在步骤 3) 中,处理程序是同步添加的,那么为什么在我的第一个例子中,处理程序从未触发?考虑到 promise.catch(err => alert('caught')); 在微任务队列中且优先级应该低于常规代码,因此它应该在运行 promise.catch 之前被触发。 - Joji
不,promise.catch(...) 在你的第一个例子中也是同步运行的。 - Jonas Wilms
promise.then(...) 不应该通过微任务队列,而是在当前代码之后运行?所以我认为 promise.catch(...) 也应该是一样的。 - Joji
你混淆了回调函数添加的时间和执行的时间。看一下编辑,希望它有助于澄清事情。 - Jonas Wilms
谢谢你的回答!但我仍然无法理解我原始问题中的第一个示例。正如你所说:“ Promise.resolve().then(function callback() { }); Promise 解析发生在微任务中,因此回调将在一次后执行。”那么我认为相同的情况也适用于 promise.catch(err => alert('caught'));。捕获将在所有当前同步代码执行完之后发生。那么在 window.addEventListener('unhandledrejection', event => alert(event.reason)); 的情况下应该捕获错误。(我指的是第一个示例) - Joji
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接