Asynclocalstorage

此类创建通过异步操作保持一致的存储。

虽然你可以在 node:async_hooks 模块之上创建自己的实现,但 AsyncLocalStorage 应该是首选,因为它是一种高性能且内存安全的实现,涉及实现起来并不明显的重要优化。

以下示例使用 AsyncLocalStorage 构建一个简单的记录器,它为传入的 HTTP 请求分配 ID,并将它们包含在每个请求中记录的消息中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import http from 'node:http';
import { AsyncLocalStorage } from 'node:async_hooks';

const asyncLocalStorage = new AsyncLocalStorage();

function logWithId(msg) {
const id = asyncLocalStorage.getStore();
console.log(`${id !== undefined ? id : '-'}:`, msg);
}

let idSeq = 0;
http.createServer((req, res) => {
asyncLocalStorage.run(idSeq++, () => {
logWithId('start');
// Imagine any chain of async operations here
setImmediate(() => {
logWithId('finish');
res.end();
});
});
}).listen(8080);

http.get('http://localhost:8080');
http.get('http://localhost:8080');
// Prints:
// 0: start
// 1: start
// 0: finish
// 1: finish

AsyncLocalStorage 的每个实例都维护一个独立的存储上下文。 多个实例可以安全地同时存在,而不会有干扰彼此数据的风险。

静态方法

新增于 18.16.0

AsyncLocalStorage.bind(fn)

fn 绑定到当前执行上下文的函数

返回 在捕获执行上下文中调用fn的新函数

AsyncLocalStorage.snapshot()

返回 带有签名 (fn: (...args) : R, ...args) : R 的新函数

捕获当前执行上下文并返回一个参数为函数的新函数。每当调用返回的函数,都会在捕获的上下文中调用传入的函数:

1
2
3
4
const asyncLocalStorage = new AsyncLocalStorage();
const runInAsyncScope = asyncLocalStorage.run(123, () => AsyncLocalStorage.snapshot());
const result = asyncLocalStorage.run(321, () => runInAsyncScope(() => asyncLocalStorage.getStore()));
console.log(result); // returns 123

AsyncLocalStorage.snapshot() 可以代替 AsyncResource 用于简单的异步上下文跟踪目的,例如:

1
2
3
4
5
6
7
8
9
10
class Foo{
#runInScope = AsyncLocalStorage.snapshot()

get(){return this.#runInScope(()=>asyncLocalStorage.getStore())}
}

const foo = asyncLocalStorage.run(789,()=>new Foo())

// print 789
console.log(asyncLocalStorage.run(8910,()=>foo.get()));

实例方法

asyncLocalStorage.disable()

禁用 AsyncLocalStorage 的实例。 对 asyncLocalStorage.getStore() 的所有后续调用都将返回 undefined,直到再次调用 asyncLocalStorage.run()asyncLocalStorage.enterWith()

调用 asyncLocalStorage.disable() 时,将退出所有当前链接到该实例的上下文。

在可以对 asyncLocalStorage 进行垃圾回收之前,需要调用 asyncLocalStorage.disable()。 这不适用于 asyncLocalStorage 提供的存储,因为这些对象与相应的异步资源一起被垃圾回收。

asyncLocalStorage 在当前进程中不再使用时使用此方法。

asyncLocalStorage.getStore()

返回当前存储。 如果在通过调用 asyncLocalStorage.run()asyncLocalStorage.enterWith() 初始化的异步上下文之外调用,它将返回 undefined

asyncLocalStorage.enterWith(store)

转换为当前同步执行的剩余部分的上下文,然后通过任何后续异步调用持久保存存储。

示例1:

1
2
3
4
5
6
7
const store = { id: 1 };
// Replaces previous store with the given store object
asyncLocalStorage.enterWith(store);
asyncLocalStorage.getStore(); // Returns the store object
someAsyncOperation(() => {
asyncLocalStorage.getStore(); // Returns the same object
});

此转换将持续整个同步执行。 这意味着,例如,如果在事件处理程序中输入上下文,则后续事件处理程序也将在该上下文中运行,除非使用 AsyncResource 专门绑定到另一个上下文。 这就是为什么 run() 应该优于 enterWith() 的原因,除非有充分的理由使用后一种方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const emitter = new EventEmitter()

emitter.on('my-event',()=>{
asyncLocalStorage.enterWith({a:1})
})

emitter.on('my-event',()=>{
const result= asyncLocalStorage.getStore()
// print {a:1}
console.log('1111',result);
})
// print undefined
console.log('2222',asyncLocalStorage.getStore());
emitter.emit('my-event')
// print {a:1}
console.log('3333',asyncLocalStorage.getStore());

asyncLocalStorage.run(store, callback[, …args])

  • store any
  • callback 回调函数
  • …args 后续参数

在上下文中同步运行函数并返回其返回值。 在回调函数之外无法访问该存储。 在回调中创建的任何异步操作都可以访问该存储。

可选的 args 被传递给回调函数。

如果回调函数抛出错误,则 run() 也会抛出该错误。 堆栈跟踪不受此调用的影响,上下文已退出。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
const store = { id: 2 };
try {
asyncLocalStorage.run(store, () => {
asyncLocalStorage.getStore(); // Returns the store object
setTimeout(() => {
asyncLocalStorage.getStore(); // Returns the store object
}, 200);
throw new Error();
});
} catch (e) {
asyncLocalStorage.getStore(); // Returns undefined
// The error will be caught here
}

asyncLocalStorage.exit(callback[, …args])

  • callback 回调函数
  • …args 后续参数

在上下文之外同步运行函数并返回其返回值。 该存储在回调函数或回调中创建的异步操作中不可访问。 在回调函数内完成的任何 getStore() 调用将始终返回 undefined

可选的 args 被传递给回调函数。

如果回调函数抛出错误,则 exit() 也会抛出该错误。 堆栈跟踪不受此调用的影响,并且重新进入上下文。

示例:

1
2
3
4
5
6
7
8
9
10
11
// Within a call to run
try {
asyncLocalStorage.getStore(); // Returns the store object or value
asyncLocalStorage.exit(() => {
asyncLocalStorage.getStore(); // Returns undefined
throw new Error();
});
} catch (e) {
asyncLocalStorage.getStore(); // Returns the same object or value
// The error will be caught here
}