非同期JavaScriptが抱える「隠れた課題」を解く
TC39が提案するAsync Contextは、JavaScriptの非同期処理で値を暗黙的に伝播させるAPI群です。現在Stage 2の段階で、言語仕様への組み込みに向けて議論が進行しています。
背景として、JavaScriptの同期処理では「暗黙的な値の伝播」が自然に機能します。関数が共有変数を参照できたり、スコープ内でアクセス可能な値があれば、コード全体で一貫性が保たれます。しかし非同期処理(Promise・async/await)では話が変わります。
awaitを境に呼び出しスタックが置き換わり、元の文脈にアクセスできなくなる—これが非同期JavaScriptの根本的な課題です。
わかりやすい例を挙げると、synchronousなコードでは以下のように機能します:
function program() {
const value = { key: 123 };
try {
shared = value; // 共有変数に値をセット
implicit(); // 関数内でsharedにアクセス可能
} finally {
shared = undefined;
}
}
let shared;
function implicit() {
console.log(shared.key); // 123が出力される
}
program();
しかしこれを非同期にすると:
async function implicit() {
console.log(shared.key); // ここまではOK(123)
await 1; // awaitで実行が中断
console.log(shared.key); // エラー!sharedはundefinedになっている
}
async/awaitの登場により、コード見た目は同期処理に近くなりました。しかしその裏側では呼び出しスタックの置換が起きており、暗黙的な文脈が失われているのです。ユーザーランドコードだけでは解決できない構造的な問題といえます。
Async Contextが提供する3つのキー概念
この提案は以下の要素で非同期文脈の保持を実現します:
- AsyncContext.Variable:非同期コード全体で共有される変数を定義・管理するAPI
- AsyncContext.Snapshot:特定時点の文脈をスナップショットとして保存・復元する機能
- Web API統合:Promise、fetch、setTimeoutなどのブラウザAPIに自動伝播
従来のアプローチ(手動でコンテキストを渡す)と比べ、開発者が明示的にパラメータ渡しをしなくても、値が自動的に非同期スコープ内に引き継がれるイメージです。
| 観点 | 従来(手動管理) | Async Context |
|---|---|---|
| コンテキスト設定 | 関数パラメータで明示的に渡す | AsyncContext.Variableで暗黙的に伝播 |
| async/await境での保持 | 失われる可能性が高い | 自動保持される |
| 記述量 | パラメータ増加で煩雑 | シンプルに保たれる |
| 実行時オーバーヘッド | 最小 | エンジン最適化に期待 |
この仕組みが実装されれば、エンジニアは「非同期境界を越えて値が保持される」ことを言語レベルで前提にできるようになります。
実務的なユースケースとフレームワークへの影響
Async Contextが活躍する場面は多岐にわたります。提案ドキュメントでは以下が明記されています:
- 分散トレーシング:リクエストIDやトレースコンテキストを、マイクロサービス全体で保持
- ロギング・モニタリング:ユーザーセッションIDをログ出力に自動埋め込み
- リクエストスコープ:Express・Fastifyなどのフレームワークがリクエストごとのコンテキスト管理を簡素化
- セキュリティ・認証:認証済みユーザー情報を非同期チェーン全体で利用可能に
個人的には、このアプローチはPythonのcontextvarsやGoのcontext.Contextに着想を得ているのだろうと感じます。言語を超えて「非同期処理での文脈伝播」という課題に共通解を模索する動きは、JavaScriptエコシステムの成熟度を示す良い指標ではないでしょうか。
Stage 2から標準化までのロード
現在Stage 2という段階は、提案の基本設計がTC39コミュニティで受け入れられ、API仕様の詳細調整フェーズにあることを意味します。提案リポジトリではMatrix roomでの隔週討議も行われており、実装者・フレームワーク開発者からのフィードバックが積極的に集められています。
Stage 3(候補段階)に進むには、仕様の安定性と実装可能性の実証が必要です。その後、複数のエンジン(V8・SpiderMonkey・JavaScriptCore)での実装を経て、Stage 4(標準化)に至る流れが一般的です。早くて数年単位での実装が期待できそうです。
ただし非同期スケジューリングやエラーハンドリングとの境界線については、まだ議論の余地があるとのこと。提案ドキュメントに「non-goals」として明記されていることから、スコープ絞りとのバランスを慎重に取っているのがわかります。