我已经破坏了 Node.js!
我正在使用异步钩子 API,我的代码使 Node.js 异常终止。
我的问题是:这段代码是什么让 Node.js 以这种方式终止,我可以在代码中更改什么来解决问题吗?
我的应用程序Data-Forge Notebook需要能够跨 JavaScript 笔记本的评估跟踪异步操作,以了解笔记本的评估何时完成。
因此,我创建了一个名为AsyncTracker的 JavaScript 类,它包装了异步钩子 API,以便我可以为一段代码启用异步跟踪。在代码部分的最后,我可以禁用跟踪并等待当前的异步操作完成。
要初始化跟踪,我这样做:
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;
}
}
}
注意代码行this.asyncOperationsAwaitResolver()
,这是触发我们在代码部分末尾等待的承诺的解决,以等待未决异步操作的完成。
禁用跟踪然后等待挂起的异步操作完成的函数如下所示:
awaitCurrentAsyncOperations() {
// At this point we stop tracking new async operations.
// We don't care about any async op started after this point.
this.trackAsyncOperations = false;
let promise;
if (this.asyncOperations.size > 0) {
promise = new Promise(resolve => {
// Extract the resolve function so we can call it when all current async operations have completed.
this.asyncOperationsAwaitResolver = resolve;
});
}
else {
this.asyncOperationsAwaitResolver = undefined;
promise = Promise.resolve();
}
return promise;
}
总而言之,这是一个使用跟踪器使 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 正常终止)。只有当我将await
关键字添加到组合中时它才会失败。因此,例如,如果我用对它await Promise.resolve()
的调用替换setTimeout
它,它会按预期工作。
GitHub上有一个这样的工作示例:
https://github.com/ashleydavis/nodejs-async-tracking-example
运行该代码以使 Node.js 爆炸。要复制克隆 repo,请运行npm install
,然后运行npm start
.
此代码已在 Windows 10 上使用 Node.js 版本 8.9.4、10.15.2 和 12.6.0 进行了测试。
此代码现已在 MacOS v8.11.3、10.15.0 和 12.6.0 上进行了测试。
它在所有测试版本上具有相同的行为。