面试题答案
一键面试实现思路
- 引入
async_hooks
模块:该模块提供了跟踪异步资源生命周期的能力。 - 创建
AsyncHook
实例:利用async_hooks.createHook
方法,在其回调函数中捕获异步任务的各种事件,如init
(初始化)、destroy
(销毁)等。 - 跟踪错误:在异步操作执行过程中,当捕获到错误时,将错误信息与对应的异步任务关联起来。
- 记录执行顺序:可以通过在
init
事件中为每个异步任务分配一个唯一标识符,并结合一个队列来记录异步任务的执行顺序。 - 整理输出:在所有异步任务完成后,按照记录的顺序输出错误信息。
关键代码片段
const asyncHooks = require('async_hooks');
const { performance } = require('perf_hooks');
// 用于存储异步任务及其错误信息
const asyncTasks = new Map();
// 用于记录异步任务执行顺序
const taskOrder = [];
const asyncHook = asyncHooks.createHook({
init(asyncId, type, triggerAsyncId, resource) {
// 为每个异步任务分配唯一标识符并记录到执行顺序队列
taskOrder.push(asyncId);
asyncTasks.set(asyncId, { type, error: null });
},
destroy(asyncId) {
// 异步任务结束,这里可以做清理等操作
},
promiseRejectWithValue(asyncId, reason) {
// 捕获Promise拒绝(错误)情况
if (asyncTasks.has(asyncId)) {
asyncTasks.get(asyncId).error = reason;
}
}
});
asyncHook.enable();
// 模拟多层嵌套异步操作和异步循环
async function nestedAsyncOperations() {
try {
await new Promise((resolve) => setTimeout(resolve, 100));
for (let i = 0; i < 3; i++) {
await new Promise((resolve, reject) => {
setTimeout(() => {
if (i === 1) {
reject(new Error('模拟错误'));
} else {
resolve();
}
}, 100);
});
}
await new Promise((resolve) => setTimeout(resolve, 100));
} catch (error) {
// 捕获外层错误
const currentAsyncId = asyncHooks.executionAsyncId();
if (asyncTasks.has(currentAsyncId)) {
asyncTasks.get(currentAsyncId).error = error;
}
} finally {
// 所有异步任务完成后整理输出错误信息
const errorsInOrder = taskOrder.map(asyncId => asyncTasks.get(asyncId).error)
.filter(error => error);
console.log('按照执行顺序的错误信息:', errorsInOrder);
}
}
nestedAsyncOperations();
在上述代码中:
- 首先引入了
async_hooks
和perf_hooks
模块。 - 创建了
asyncHook
实例并注册了init
、destroy
和promiseRejectWithValue
等回调函数。init
用于初始化异步任务跟踪,destroy
可用于清理,promiseRejectWithValue
捕获Promise的拒绝错误。 nestedAsyncOperations
函数模拟了多层嵌套异步操作和异步循环。在循环中模拟了错误抛出。- 最后在
finally
块中,按照记录的异步任务执行顺序整理并输出错误信息。