コンテンツにスキップ

AsyncLocalStorage

背景

Cloudflare Workersは、非同期操作を通じて一貫性を保つメモリ内ストアを作成するためのNode.jsのAsyncLocalStorage APIのサブセットの実装を提供します。

コンストラクタ

import { AsyncLocalStorage } from 'node:async_hooks';
const asyncLocalStorage = new AsyncLocalStorage();
  • new AsyncLocalStorage() : AsyncLocalStorage

    • 新しいAsyncLocalStorageインスタンスを返します。

メソッド

  • getStore() : any

    • 現在のストアを返します。asyncLocalStorage.run()を呼び出して初期化された非同期コンテキストの外で呼び出された場合、undefinedを返します。
  • run(storeany, callbackfunction, …argsarguments) : any

    • コンテキスト内で関数を同期的に実行し、その戻り値を返します。ストアはコールバック関数の外ではアクセスできません。ストアはコールバック内で作成された任意の非同期操作にアクセス可能です。オプションのargsはコールバック関数に渡されます。コールバック関数がエラーをスローした場合、run()もエラーをスローします。
  • exit(callbackfunction, …argsarguments) : any

    • コンテキストの外で関数を同期的に実行し、その戻り値を返します。このメソッドは、storeの値をundefinedに設定してrun()を呼び出すことと同等です。

静的メソッド

  • AsyncLocalStorage.bind(fn) : function

    • bind()が呼び出されたときの現在の非同期コンテキストをキャプチャし、渡された関数を呼び出す前にそのコンテキストに入る関数を返します。
  • AsyncLocalStorage.snapshot() : function

    • snapshot()が呼び出されたときの現在の非同期コンテキストをキャプチャし、指定された関数を呼び出す前にそのコンテキストに入る関数を返します。

フェッチリスナー

import { AsyncLocalStorage } from 'node:async_hooks';
const asyncLocalStorage = new AsyncLocalStorage();
let idSeq = 0;
export default {
async fetch(req) {
return asyncLocalStorage.run(idSeq++, () => {
// 一部の非同期アクティビティをシミュレート...
await scheduler.wait(1000);
return new Response(asyncLocalStorage.getStore());
});
}
};

複数のストア

APIは、複数のAsyncLocalStorageインスタンスを同時に使用することをサポートしています。

import { AsyncLocalStorage } from 'node:async_hooks';
const als1 = new AsyncLocalStorage();
const als2 = new AsyncLocalStorage();
export default {
async fetch(req) {
return als1.run(123, () => {
return als2.run(321, () => {
// 一部の非同期アクティビティをシミュレート...
await scheduler.wait(1000);
return new Response(`${als1.getStore()}-${als2.getStore()}`);
});
});
}
};

未処理の拒否

Promiseが拒否され、拒否が未処理の場合、非同期コンテキストは'unhandledrejection'イベントハンドラーに伝播します:

import { AsyncLocalStorage } from 'node:async_hooks';
const asyncLocalStorage = new AsyncLocalStorage();
let idSeq = 0;
addEventListener('unhandledrejection', (event) => {
console.log(asyncLocalStorage.getStore(), '未処理の拒否!');
});
export default {
async fetch(req) {
return asyncLocalStorage.run(idSeq++, () => {
// 未処理の拒否を引き起こす!
throw new Error('boom');
});
}
};

AsyncLocalStorage.bind()AsyncLocalStorage.snapshot()

import { AsyncLocalStorage } from 'node:async_hooks';
const als = new AsyncLocalStorage();
function foo() { console.log(als.getStore()); }
function bar() { console.log(als.getStore()); }
const oneFoo = als.run(123, () => AsyncLocalStorage.bind(foo));
oneFoo(); // 123を出力
const snapshot = als.run('abc', () => AsyncLocalStorage.snapshot());
snapshot(foo); // 'abc'を出力
snapshot(bar); // 'abc'を出力
import { AsyncLocalStorage } from 'node:async_hooks';
const als = new AsyncLocalStorage();
class MyResource {
#runInAsyncScope = AsyncLocalStorage.snapshot();
doSomething() {
this.#runInAsyncScope(() => {
return als.getStore();
});
}
};
const myResource = als.run(123, () => new MyResource());
console.log(myResource.doSomething()); // 123を出力

AsyncResource

AsyncResourceクラスは、ユーザーが独自の非同期コンテキストを作成できるNode.jsの非同期コンテキスト追跡APIのコンポーネントです。AsyncResourceから拡張されたオブジェクトは、プロミスと同様に非同期コンテキストを伝播することができます。

AsyncLocalStorage.snapshot()AsyncLocalStorage.bind()がより良いアプローチを提供することに注意してください。AsyncResourceはNode.jsとの後方互換性のために提供されています。

コンストラクタ

import { AsyncResource, AsyncLocalStorage } from 'node:async_hooks';
const als = new AsyncLocalStorage();
class MyResource extends AsyncResource {
constructor() {
// タイプ文字列はNode.jsによって要求されますが、Workersでは使用されません。
super('MyResource');
}
doSomething() {
this.runInAsyncScope(() => {
return als.getStore();
});
}
};
const myResource = als.run(123, () => new MyResource());
console.log(myResource.doSomething()); // 123を出力
  • new AsyncResource(typestring, optionsAsyncResourceOptions) : AsyncResource

    • 新しいAsyncResourceを返します。重要なことに、コンストラクタ引数はNode.jsのAsyncResourceの実装では必要ですが、Workersでは使用されません。
  • AsyncResource.bind(fnfunction, typestring, thisArgany)
    • 指定された関数を現在の非同期コンテキストにバインドします。

メソッド

  • asyncResource.bind(fnfunction, thisArgany)
    • 指定された関数をこのAsyncResourceに関連付けられた非同期コンテキストにバインドします。
  • asyncResource.runInAsyncScope(fnfunction, thisArgany, …argsarguments)
    • 指定された引数で提供された関数をこのAsyncResourceに関連付けられた非同期コンテキストで呼び出します。

注意事項

  • Workersによって提供されるAsyncLocalStorageの実装は、意図的にasyncLocalStorage.enterWith()およびasyncLocalStorage.disable()メソッドのサポートを省略しています。

  • Workersは、Node.jsのAsyncLocalStorageの実装が構築されている完全なasync_hooks APIを実装していません。

  • Workersは、Node.jsによって許可される明示的に識別されたトリガーコンテキストを持つAsyncResourceを作成する機能を実装していません。これは、新しいAsyncResourceが常に作成された非同期コンテキストにバインドされることを意味します。

  • Thenables(then()メソッドを公開する非Promiseオブジェクト)は、AsyncLocalStorageを使用する際に完全にはサポートされていません。Thenablesを扱う場合は、代わりにAsyncLocalStorage.snapshot()を使用して現在のコンテキストのスナップショットをキャプチャしてください。