Javascript 为什么我使用异步钩子API会导致Node.js异常终止?
我已经破坏了Node.js 我正在使用,我的代码使Node.js异常终止 我的问题是:这段代码是什么让Node.js以这种方式终止的?在代码中有什么我可以修改的地方来修复这个问题吗 我的应用程序需要能够在JavaScript笔记本的评估过程中跟踪异步操作,以了解笔记本的评估何时完成 因此,我创建了一个名为的JavaScript类,它包装了异步钩子API,因此我可以对一段代码启用异步跟踪。在代码部分的末尾,我可以禁用跟踪并等待当前异步操作完成 要初始化跟踪,请执行以下操作:Javascript 为什么我使用异步钩子API会导致Node.js异常终止?,javascript,node.js,asynchronous,async-await,async-hooks,Javascript,Node.js,Asynchronous,Async Await,Async Hooks,我已经破坏了Node.js 我正在使用,我的代码使Node.js异常终止 我的问题是:这段代码是什么让Node.js以这种方式终止的?在代码中有什么我可以修改的地方来修复这个问题吗 我的应用程序需要能够在JavaScript笔记本的评估过程中跟踪异步操作,以了解笔记本的评估何时完成 因此,我创建了一个名为的JavaScript类,它包装了异步钩子API,因此我可以对一段代码启用异步跟踪。在代码部分的末尾,我可以禁用跟踪并等待当前异步操作完成 要初始化跟踪,请执行以下操作: this.asyncH
this.asyncHook = async_hooks.createHook({
init: (asyncId, type, triggerAsyncId, resource) => {
this.addAsyncOperation(asyncId, type);
},
after: asyncId => {
this.removeAsyncOperation(asyncId);
},
destroy: asyncId => {
this.removeAsyncOperation(asyncId);
},
promiseResolve: asyncId => {
this.removeAsyncOperation(asyncId);
},
});
this.asyncHook.enable();
异步操作记录在JS映射中,但它们仅在通过将trackAsyncOperations
设置为true
启用跟踪时添加。此变量允许在代码段开始时启用跟踪:
addAsyncOperation(asyncId, type) {
if (this.trackAsyncOperations) {
this.asyncOperations.add(asyncId);
this.openAsyncOperations.set(asyncId, type);
}
}
各种异步挂钩导致从映射中删除异步操作:
removeAsyncOperation(asyncId) {
if (this.asyncOperations.has(asyncId)) {
this.asyncOperations.delete(asyncId);
this.openAsyncOperations.delete(asyncId);
if (this.asyncOperationsAwaitResolver &&
this.asyncOperations.size <= 0) {
this.asyncOperationsAwaitResolver();
this.asyncOperationsAwaitResolver = undefined;
}
}
}
总而言之,这里有一个使用跟踪程序使Node.js在没有警告的情况下中止的最小示例:
const asyncTracker = new AsyncTracker();
asyncTracker.init();
asyncTracker.enableTracking(); // Enable async operation tracking.
// ---- Async operations created from here on are tracked.
// The simplest async operation that causes this problem.
// If you comment out this code the program completes normally.
await Promise.resolve();
// --- Now we disable tracking of async operations,
// then wait for all current operations to complete before continuing.
// Disable async tracking and wait.
await asyncTracker.awaitCurrentAsyncOperations();
请注意,这一代码并不是一刀切的。当与基于回调或基于承诺的异步操作一起使用时,它似乎工作正常(Node.js正常终止)。只有当我将wait
关键字添加到混合中时,它才会失败。例如,如果我用调用setTimeout
替换wait Promise.resolve()
,它将按预期工作
GitHub上有一个这样的工作示例:
运行该代码使Node.js爆炸。要复制克隆repo,请运行npm安装
,然后运行npm启动
此代码已在Windows 10上测试,Node.js版本为8.9.4、10.15.2和12.6.0
此代码现已在MacOS v8.11.3、10.15.0和12.6.0上进行了测试
它在所有测试版本上都具有相同的行为。代码审查
在查看GitHub上的完整代码并在Windows 10上使用节点v10.16.0运行它之后,它看起来像是在AsyncTracker中返回的承诺。WaitCurrentAsyncOperations
从未解决过,这会阻止main()
中的代码超越Wait AsyncTracker.WaitCurrentAsyncOperations()代码>零件
这解释了为什么**33**
部分从未输出,以及main()
的然后(…)
回调从未打印出Done
。所述承诺的解析方法被分配给this.asyncOperationsWaitResolver
,但是(根据当前实现),只有当this.asyncOperation
集中没有更多承诺时,才会调用它。如下面的第一控制台输出所示,情况并非如此
问题再生产
我稍微修改了代码以处理process.onindex.js
脚本末尾的事件
process.on('exit',()=>{console.log('processexited')});
process.on('uncaughtException',(err)=>{console.error(err&&err.stack | | err)});
process.on('unhandledRejection',(reason,promise)=>{console.error('unhandledRejection at:',promise',reason:',reason:',reason});
process.on('multipleResolved',(type,promise,reason)=>{console.error(type,promise,reason)});
退出回调是唯一被调用的回调。**22**标记后的控制台输出为:
**22**
>>>>>>>单元格已结束,异步操作跟踪已禁用,当前有3个异步操作正在进行。
等待操作完成,创建承诺。
!! 还有3个异步操作:
#9-承诺。
#10-承诺。
#11-承诺。
%%已删除异步操作#9
!! 还有2个异步操作:
#10-承诺。
#11-承诺。
进程退出
解决方案
问题是由removeAsyncOperation(asyncId)
方法中的键入错误引起的。
而不是:
removeAsyncOperation(异步ID){
// ...
if(this.asyncOperationsWaitingResolver&&this.asyncOperations.size=0){
//...
}
}
}
这样,只要队列中有承诺,就可以解析承诺
在对async tracker.js
进行上述更改后,应用程序的行为与预期一致,并生成以下输出:
**22**
>>>>>>>单元格已结束,异步操作跟踪已禁用,当前有3个异步操作正在进行。
等待操作完成,创建承诺。
!! 还有3个异步操作:
#9-承诺。
#10-承诺。
#11-承诺。
%%已删除异步操作#9
!! 还有2个异步操作:
#10-承诺。
#11-承诺。
%%解析异步操作承诺!
** 33 **
多恩
%%已删除异步操作#10
!! 还有1个异步操作:
#11-承诺。
%%已删除异步操作#11
!! 剩余0个异步操作:
进程退出
好的,我现在有答案了。我会继续更新这个,因为我更好地理解我造成的这个问题
我的回答还没有完全解释Node.js的操作方式,但它是一种解决方案
我的代码试图等待在一段代码中发生的任意异步操作完成。在我的代码示例中,异步操作的跟踪发生在main
函数中,而then
和catch
处理程序发生在main
函数之外。我的理论是,被跟踪的异步操作被永远不会执行的代码“保持活动”:导致异步操作完成的代码永远不会执行
我通过删除然后
和catch
回调发现了这一点,这使我的程序正常终止
所以我的工作原理是,我导致了某种Node.js promis
const asyncTracker = new AsyncTracker();
asyncTracker.init();
asyncTracker.enableTracking(); // Enable async operation tracking.
// ---- Async operations created from here on are tracked.
// The simplest async operation that causes this problem.
// If you comment out this code the program completes normally.
await Promise.resolve();
// --- Now we disable tracking of async operations,
// then wait for all current operations to complete before continuing.
// Disable async tracking and wait.
await asyncTracker.awaitCurrentAsyncOperations();