Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

AsyncLocalStorage

最終更新 Markdown で表示Agent セットアップ

背景

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() 呼び出し時点の非同期コンテキストをキャプチャし、指定した関数を呼ぶ前にそのコンテキストへ入る関数を返します。

Fetch リスナー

import { AsyncLocalStorage } from 'node:async_hooks';

const asyncLocalStorage = new AsyncLocalStorage();
let idSeq = 0;

export default {
  async fetch(req) {
    return asyncLocalStorage.run(idSeq++, () => {
      // Simulate some async activity...
      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, () => {
        // Simulate some async activity...
        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(), "unhandled rejection!");
});

export default {
	async fetch(req) {
		return asyncLocalStorage.run(idSeq++, () => {
			// Cause an unhandled rejection!
			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(); // prints 123

const snapshot = als.run("abc", () => AsyncLocalStorage.snapshot());
snapshot(foo); // prints 'abc'
snapshot(bar); // prints '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()); // prints 123

AsyncResource

AsyncResource クラスは、Node.js の非同期コンテキスト追跡 API の一部で、独自の非同期コンテキストを作れます。AsyncResource を拡張したオブジェクトは、Promise とほぼ同じ方法で非同期コンテキストを伝播できます。

よりよい方法は AsyncLocalStorage.snapshot()AsyncLocalStorage.bind() です。AsyncResource は、Node.js との後方互換性のためだけに提供されています。

コンストラクター

import { AsyncResource, AsyncLocalStorage } from "node:async_hooks";

const als = new AsyncLocalStorage();

class MyResource extends AsyncResource {
	constructor() {
		// The type string is required by Node.js but unused in Workers.
		super("MyResource");
	}

	doSomething() {
		this.runInAsyncScope(() => {
			return als.getStore();
		});
	}
}

const myResource = als.run(123, () => new MyResource());
console.log(myResource.doSomething()); // prints 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 は常に、作成時の非同期コンテキストにバインドされます。

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

役に立ちましたか?