javascript 如何在后期的 .catch 处理程序中捕获异常?

How does javascript manage to catch exception in late .catch handler?

前几天我遇到了以下和平代码:

let promise = Promise.reject(new Error('Promise Failed!')); // 1

setTimeout(() => promise.catch(err => alert('caught')), 1000); // 2

// 3

所以我很惊讶地发现错误是在错误发生一秒后注册的处理程序中捕获的。

现在,让我逐步解释我的看法,以便您纠正我并告诉我我错在哪里:

  1. 在执行整个脚本的 current 宏任务期间,承诺拒绝 (1) 并且因为没有注册拒绝handler 控件依次移动到下一行。
  2. 我们调用 setTimeout 向其传递一个回调函数,其中要注册拒绝处理程序 (2)。但是这个回调只会在第二次延迟后被安排。此外,它将在 单独的 任务中执行。
  3. 执行在线 3current宏任务完成,堆栈和任务队列都为空。剩下的就是等待给定的 1 秒延迟。
  4. 一秒超时完成。我们的回调到达任务队列并立即被推送到有效运行它的堆栈。
  5. 在回调函数的执行过程中,它是 new 任务的一部分,.catch 处理程序为一秒前被拒绝的承诺注册,这发生了在 上一个 和已经完成的任务中。尽管如此,它还是成功捕获了错误。

这是否意味着一直以来错误一直在内存中的某个地方等待可能 .catch 处理程序稍后注册的机会?但是,如果它从未发生过并且被拒绝的承诺数量要多得多呢?未处理的错误是否会在内存中保留 'hang' 以等待其处理程序被注册?

直接回答你的问题:

Does this mean that all this time the error had been somewhere in memory waiting for a chance that maybe .catch handler would be registered later?

是的,我们有一个 WeakMap 待处理的拒绝记录,用于跟踪被拒绝但未同步处理的承诺。 Chrome 做类似的事情(如果你愿意,我可以 link)。

But what if it had never happened and the number of rejected promises had been a lot more?

这些功能的设计假设是拒绝非常罕见并且是针对特殊情况 - 因此拒绝路径有点慢。但是,是的,理论上您可以创建很多“未决”拒绝。

Will unhandled errors remain to 'hang' in memory waiting for its handler to be registered?

我们只等待 microtick - 所以流程是:

  • 您创建了一个被拒绝的承诺
  • 它没有处理程序,所以它被放入 WeakMap
  • 所有微任务都是运行 (process.nextTick/Promise.resolve.then)
  • 如果它仍然被拒绝,这是一个未处理的拒绝并且它被记录到屏幕/事件被触发。

也就是说,拒绝不会在“内存中”保留 1000 毫秒(计时器持续时间),而是只要微任务完成 运行。

Does this mean that all this time the error had been somewhere in memory waiting for a chance that maybe .catch handler would be registered later?

是的,它存储在 promise 对象中。请注意,承诺是 not just a notification mechanism,它们旨在表示 one-off 异步任务的 结果 。履行价值或拒绝原因(在您的情况下,Error 实例)和解决状态在结算时存储在承诺中。

这背后的主要思想是 .then.catch 处理程序将始终使用结果值调用,无论 .then() 调用发生在承诺。这也允许多个处理程序。

Will unhandled errors remain to 'hang' in memory waiting for its handler to be registered?

如果您没有 setTimeoutlet promise 变量及其承诺对象和错误对象将立即 garbage-collected。