Ree*_*erg 5 javascript node.js typescript
除了通常的嫌疑人(process.exit(),或进程终止/信号,或崩溃/硬件故障)之外,是否存在无法到达finally块中的代码的情况?
以下打字稿代码通常按预期执行(使用node.js),但有时会在第 4 行立即终止,并且不会引发任何异常,也不会更改进程退出代码(退出 0/成功):
1 import si from 'systeminformation';
2 async populateResolvedValue() {
3 try {
4 const osInfo = await si.osInfo();
5 ...
6 } finally {
7 console.log('whew!'); // <=========== NOT REACHED!
8 }
9 }
Run Code Online (Sandbox Code Playgroud)
我已经在 IJ 调试会话中验证了这一点 -finally第 7 行的块有时不会执行,并且会在第 4 行立即终止(对堆栈进行一些展开)。我知道这种情况可能发生(并且仍然成功退出)的唯一情况是,如果在某处遇到段错误someAsyncFunc(),但我添加了“段错误处理程序”,但那里没有任何显示。
我也尝试过使用 Promise.then/finally 而不是 async/await 和 try/finally 语义 - 完全相同的行为。
Node.js:v12.18.2 和 v14.16.0
感谢所有评论,我能够解决核心问题并找到满意的答案。
简而言之,是的 - 除了通常的嫌疑人之外,Promise.finally如果所讨论的承诺永远不会“解决”,那么(或带有等待的 try/finally)块可能永远不会到达并执行。有些 Promise 可能需要比预期更长的时间才能解决(I/O 等待等),并最终解决或拒绝,但如果 Promise从未解决(由于错误或设计),则依赖于该 Promise 的所有代码(then、finally等)将永远不会解决执行。
那么什么是未解决的承诺,它们是如何发生的呢?如果承诺resolve或reject回调函数从未被调用(由于错误或设计),则承诺可能永远不会解决。
考虑以下承诺:
1 iPromiseTo(): Promise<any> {
2 return new Promise((resolve, reject) => {
3 if (this.willNeverHappen()) {
4 resolve('I can forp'); <====== Resolved
5 } else if (this.willAlsoNeverHappen()) {
6 reject('I cannot forp but I can rarp'); <====== Rejected
7 } else {
8 console.log('I cannot promise anything'); <====== Unsettled
9 }
10 });
11 }
Run Code Online (Sandbox Code Playgroud)
如果 Promise 函数体在没有解析/拒绝的情况下完成(通过第 8 行到第 10 行),则任何依赖代码(包括稍后附加的 then/catch/finally 块(见下文))都将不会执行,并将被默默地忽略。在大多数情况下,这可能被认为是一个错误,这就是async函数在大多数情况下具有优势的原因。当函数体完成时,函数async总是会解析或拒绝(其函数体可能永远不会完成,但这是另一个主题)。
在语义中使用这个有缺陷的承诺Promise:
1
2 youllBeSorry() {
3 this.iPromiseTo()
4 .then(promised => {
5 console.log(promised); // <=========== NOT REACHED!
6 }).catch(error => {
7 console.log(error); // <=========== NOT REACHED!
8 }).finally(() =>
9 console.log('whew!'); // <=========== NOT REACHED!
10 });
11 }
Run Code Online (Sandbox Code Playgroud)
第 5、7 和 9 行将不会执行。
在 async/await 语义中使用它:
1
2 async youllBeSorry() {
3 try {
4 const promised = await this.iPromiseTo();
5 ... // <=========== NOT REACHED!
6 } catch(error) {
7 console.log(error); // <=========== NOT REACHED!
8 } finally {
9 console.log('whew!'); // <=========== NOT REACHED!
10 }
11 }
Run Code Online (Sandbox Code Playgroud)
第 5、7 和 9 行将不会执行,该函数将在第 4 行静默返回,没有错误或任何后续异常处理。
另一类可能永远不会解决的承诺是那些由于无限循环或无限挂起等(由于错误或设计)而永远不会完成的函数体。这些实际上的行为与那些完成其函数体但从不调用resolveor的 Promise 类似reject,尽管两者之间可能存在其他差异,此处未涵盖。
好吧,那么我该如何处理这些“未解决”的承诺呢?不幸的是,没有规定的方法来捕获和处理承诺的这种特殊结果(在撰写本文时)。有一些建议,但我自己没有尝试过: await/async 如何处理未解决的承诺
await有可能已经可用或即将推出的
顶级(另请参阅其较旧的 IIFE 解决方法对应项): https://v8.dev/features/top-level-await
理想情况下,ECMAScript 将为异步行为提供更多顶级控制 - 请参阅 kotlin(甚至 python)协程,以获取有关如何更好地执行异步的更强大的示例。
finally最后(没有双关语),我要指出的是,在这种特殊情况下,该块通常按预期执行的原因是正在使用 ocif 框架,不幸的是,该框架exit在其 API 例程中嵌入了调用。在所有情况下,这里所讨论的承诺都可以实现/解决,但它与 ocif 中的退出调用竞争,因此并不总是得到解决,并且依赖代码并不总是在抛出退出异常和节点退出之前运行。So 被证明是这个特殊案例中的“常见嫌疑人”之一,但故障排除导致发现了 ECMAScript 中的这些缺陷。
| 归档时间: |
|
| 查看次数: |
4943 次 |
| 最近记录: |