JavaScriptでより優れたストリームAPIを実装できる可能性があります。

2026/02/27 23:02

JavaScriptでより優れたストリームAPIを実装できる可能性があります。

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

Web Streams は、ブラウザの

fetch()
と Node.js、Deno、Bun、および Cloudflare Workers におけるサーバー側ストリーミングを可能にする標準ですが、その設計上の選択がパフォーマンスボトルネックと複雑さを生み出しています。
スペックの「リーダー取得/ロッキングモデル」、アドバイザリ・バックプレッシャー、BYOB API、および大量のプロミス生成は実際には次のような失敗につながります:レスポンスが完全に消費されないと接続プールが枯渇する、
tee()
によってメモリ崩壊(1 つの遅いブランチが無限にバッファリングできる)、
TransformStream
の書き込みで下流へ波及するイーガーバッファリング、およびサーバー側レンダリングパイプラインでリクエストあたり CPU 時間の半分以上を消費する GC プレッシャー。
実行時チームは、これらの問題を緩和するために非標準の「direct streams」または「identity transform」最適化を導入していますが、この断片化は移植性を損ないます。
ベンチマークでは、単純な async‑iterator に基づく代替案が 80〜90 倍速いことが示されています。提案された API は明示的なリーダー/ライター/ロックを排除し、厳格なバックプレッシャーを強制し、
Uint8Array[]
のチャンクをバッチ処理し、ゼロ割り当てケースのために同期高速経路を提供し、明示的なマルチコンシューマプリミティブを備えています。
このモデルを採用すると、サーバー側レンダリングとストリーミングワークロードがより速くなり、メモリ消費が減少し、Web Streams と共存または将来のランタイムで置き換えることのできる統一された高性能ストリーミングインターフェースが実現でき、JavaScript エコシステム全体の断片化を低減できます。

本文

2026‑02‑27 – 24 分読了

ストリームでデータを扱うことは、アプリケーション構築の根幹です。
ストリーミングをあらゆる場所で機能させるために、WHATWG Streams Standard(俗称 Web streams)が設計されました。この標準はブラウザとサーバー間で共通の API を確立しようというものです。ブラウザに実装された後、Cloudflare Workers、Node.js、Deno、Bun に採用され、

fetch()
などの API の基盤となりました。
これは大規模な取り組みであり、その設計者たちは当時持っていた制約とツールを駆使して難しい問題に対処していました。

Web streams を数年にわたり実装・改良し、Node.js と Cloudflare Workers で導入し、顧客やランタイムの本番障害をデバッグし、開発者が直面する多くの典型的な落とし穴を解消してきた結果、私は標準 API に根本的な使い勝手とパフォーマンス上の問題があると確信しています。これらは単なるバグではなく、10 年前には理にかなっていた設計決定が現在の JavaScript 開発者のコード記述スタイルと合致しない結果です。

本稿では Web streams に対する私が見ている根本的な問題を掘り下げ、JavaScript 言語プリミティブを中心に据えた代替アプローチを提示します。このアプローチはベンチマーク上で Cloudflare Workers, Node.js, Deno, Bun そして主要ブラウザの全てで 2 倍から 120 倍まで高速化できることが示されています。改善点は巧妙な最適化ではなく、モダン JavaScript 言語機能をより効果的に活用した本質的に異なる設計選択によるものです。


背景

Streams Standard は 2014 年から 2016 年の間に開発され、「低レベル I/O プリミティブへ効率的にマッピングできるデータストリームを作成、合成、および消費する API を提供する」という野心的な目標を掲げました。
Web streams が登場する以前、ウェブプラットフォームにはストリーミングデータを扱う標準手段はありませんでした。Node.js には当時から独自のストリーム API が存在していましたが、WHATWG は Web ブラウザだけを対象にした仕様であるため、それを出発点とはしませんでした。

Web streams の設計は JavaScript における非同期イテレーション(

for await…of
)より前のものです。
for await…of
構文は ES2018 で登場し、Streams Standard が最初に確定された 2 年後でした。このタイミング上、API は当初「非同期シーケンスを消費するための慣用的手段」を活かせませんでした。代わりに仕様は独自のリーダ/ライタ取得モデルを導入し、その決定が API のあらゆる側面へ波及しました。


共通操作への過剰な儀式

ストリームで最も頻繁に行う作業は「完了まで読み込む」ことです。Web streams では次のようになります:

// まず、ストリームを独占ロックするリーダーを取得…
const reader = stream.getReader();
const chunks = [];
try {
  // 次に read を繰り返し呼び出し、戻ってくる Promise を待つ
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    chunks.push(value);
  }
} finally {
  // 最後にストリームのロックを解放
  reader.releaseLock();
}

このパターンは「ストリーミング固有」と誤解されがちですが、実際にはリーダー取得・ロック管理・

{ value, done }
プロトコルといった設計選択に過ぎません。これらは Web streams 仕様が書かれた当時の事情によるものです。

非同期イテレーションは「時間を経て到着するシーケンス」を扱うために存在しますが、ストリーム仕様が書かれたときにはまだ存在しませんでした。ここで生じる複雑さは純粋な API オーバーヘッドであり、根本的な必要性ではありません。

Web streams が

for await…of
をサポートするようになった今の代替アプローチ:

const chunks = [];
for await (const chunk of stream) {
  chunks.push(chunk);
}

これはボイラープレートが格段に少なくなる点で優れていますが、すべてを解決しているわけではありません。非同期イテレーションは設計されていない API に後付けされたため、BYOB(自前バッファ)読み取りなどの機能はイテレーション経由では利用できません。


ロック問題

Web streams は「複数消費者が読み取りをインターリーブしない」ようにロックモデルを採用しています。

getReader()
を呼ぶとストリームはロックされ、ロック中は他のコードが直接ストリームから読み取ったりパイプしたりキャンセルしたりできません。ロックを保持しているコードだけが操作できます。

async function peekFirstChunk(stream) {
  const reader = stream.getReader();
  const { value } = await reader.read(); // ← releaseLock() を呼ばずに戻ると
  return value;
}

const first = await peekFirstChunk(stream);
// TypeError: Cannot obtain lock — stream is permanently locked
for await (const chunk of stream) { /* 実行されない */ }

実装者側ではロックモデルがかなりの内部 bookkeeping を必要とします。各操作でロック状態を確認し、リーダーを追跡し、ロック・キャンセル・エラー状態との相互作用により多くの境界ケースを正しく処理する必要があります。


BYOB:報酬のない複雑さ

BYOB(Bring Your Own Buffer)読み取りは、ストリームからデータを読む際にメモリバッファを再利用できるよう設計されました。高スループット環境で重要な最適化ですが、実務では測定可能な恩恵がほとんどありません。API はデフォルト読み取りよりも大幅に複雑で、専用リーダー型(

ReadableStreamBYOBReader
)や
ReadableStreamBYOBRequest
などの特殊クラスを必要とし、ArrayBuffer の分離(detachment)意味論を理解する必要があります。

バッファを BYOB 読み取りに渡すと、そのバッファはストリームへ転送されて「デタッチ」されます。結果として別のメモリ領域を指す新しいビューが返ってきます。この転送ベースモデルはエラーが起こりやすく、混乱しやすいです:

const reader = stream.getReader({ mode: 'byob' });
const buffer = new ArrayBuffer(1024);
let view = new Uint8Array(buffer);

const result = await reader.read(view);
// 'view' はデタッチされて使用不能になるはず
// (実装によっては必ずしもそうならない)
result.value is a NEW view, possibly over different memory
view = result.value; // 代入が必要

さらに BYOB は非同期イテレーションや

TransformStream
と併用できません。ゼロコピー読み取りを望む開発者は、手動リーダー・ループに戻ることを強いられます。

実装者側では BYOB が大きな複雑さをもたらします。ストリームは保留中の BYOB 要求を追跡し、部分的な埋め込みを処理し、バッファ分離を正しく管理し、BYOB リーダーと基盤ソース間で調整する必要があります。Readable byte streams の Web Platform Tests には、デタッチされたバッファ、悪いビュー、レスポンス後の enqueue 順序など、BYOB エッジケース専用のテストが多数含まれています。


バックプレッシャー:理論上は良いが実際には壊れている

バックプレッシャー(遅い消費者が高速プロデューサーに減速を要求できる機能)は Web streams の主要概念です。理論的には妥当ですが、実装上は深刻な欠陥があります。

主なシグナルは

desiredSize
で、プラス(データ欲しい)、ゼロ(容量満タン)、マイナス(オーバーフロー)または null(閉じた状態)が取れます。プロデューサーはこの値をチェックし、正の値でないときは enqueue を停止すべきです。しかし何も強制するものがなく、
controller.enqueue()
は常に成功します。

new ReadableStream({
  start(controller) {
    // これを止めることはできません
    while (true) {
      controller.enqueue(generateData()); // desiredSize: -999999
    }
  }
});

ストリーム実装はバックプレッシャーを無視することができますし、いくつかの仕様上定義された機能(例:

tee()
)はバックプレッシャーを破棄します。
tee()
は単一ストリームから二つの分岐を作ります。片方が他方より速く読み取ると、内部バッファにデータが溜まり、メモリ消費が無制限に増加します。高速消費者は未満速度の消費者が追いつくまで膨大なメモリを占有し続けます。

Web streams は

highWaterMark
オプションとカスタマイズ可能なサイズ計算でバックプレッシャー挙動を微調整する手段を提供しますが、これらも
desiredSize
と同様に無視されやすく、多くのアプリケーションは単にそれらを見逃しています。WritableStream 側でも同様です。WritableStream は highWaterMark と desiredSize を持ち、データプロデューサーが注意すべき
writer.ready
プロミスがありますが、実際には多くの場合無視されています。

const writable = getWritableStreamSomehow();
const writer = writable.getWriter();

// プロデューサーは writer.ready を待つべきです
await writer.ready;
await writer.write(...);

実装者側ではバックプレッシャーが追跡キューサイズ、desiredSize 計算、適切なタイミングでの pull() 呼び出しを正しく実装する必要があります。これにより複雑さが増す一方で保証は得られません。


プロミスの隠れたコスト

Web streams 仕様は多くの箇所でプロミス生成を要求しており、ホットパスで頻繁に発生し、ユーザーには見えません。

read()
呼び出しごとに単なる Promise を返すだけではなく、内部的にキュー管理や pull() コーディネーション、バックプレッシャー信号のための追加プロミスが生成されます。

このオーバーヘッドは、バッファ管理・完了通知・バックプレッシャーシグナルを Promise に依存する仕様設計に起因します。実装固有の部分もありますが、多くは仕様通りに従う限り不可避です。高頻度ストリーミング(ビデオフレーム、ネットワークパケット、リアルタイムデータ)ではこのオーバーヘッドが顕著になります。

パイプライン内での問題はさらに悪化します。各

TransformStream
はソースとシンク間に追加の Promise 機構を挿入し、データが即座に利用可能でもプロミス処理が走ります。仕様には同期高速経路が定義されていないためです。


実際の失敗例

未消費ボディでリソース枯渇

fetch()
が返すレスポンスは ReadableStream です。ステータスのみ確認し、ボディを消費もキャンセルもしなかった場合、何が起こるでしょうか?実装により結果は異なりますが、共通する問題はリソースリークです。

async function checkEndpoint(url) {
  const response = await fetch(url);
  return response.ok; // ボディは消費もキャンセルもしない
}

for (const url of urls) {
  await checkEndpoint(url);
}

ループ内でこれを繰り返すと、接続プールが枯渇します。ストリームは基盤接続への参照を保持し、明示的に消費またはキャンセルされない限り、ガベージコレクションまで接続が残ります。負荷下では十分に早く GC が起きない可能性があります。


これらの問題に対する解決策

問題対応
未消費ボディPull シグナリングで何も発生しないため、隠れたリソース保持が無い
Tee メモリ崩壊
Stream.share()
は明示的にバッファ構成を要求。高速/低速消費者の差異による無制限増加は防止
Transform バックプレッシャーギャップPull‑through 変換はオンデマンドで実行され、データは必要時のみフローし、中間バッファに流れない
SSR の GC スラッシュバッチ化されたチャンク(
Uint8Array[]
)で非同期オーバーヘッドを平滑化。CPU 集中型ワークロードでは
Stream.pullSync()
により Promise 割当を完全に排除

パフォーマンス

以下は Node.js v24.x、Apple M1 Pro で行ったベンチマーク(10 回平均)です。

シナリオ代替手法Web streams
小チャンク (1 KB × 5000)~13 GB/s~4 GB/s約3×高速
微小チャンク (100 B × 10000)~4 GB/s~450 MB/s約8×高速
非同期イテレーション (8 KB × 1000)~530 GB/s~35 GB/s約15×高速
3つの変換を連鎖 (8 KB × 500)~275 GB/s~3 GB/s約80–90×高速
高頻度 (64 B × 20000)~7.5 GB/s~280 MB/s約25×高速

特に 3 つの連鎖変換結果は顕著です。Pull‑through 植物が Web streams のパイプラインを悩ます中間バッファリングを排除しています。


今後

この投稿は議論を始めるために公開しています。
何が正しく、何が抜けているのか? このモデルに合わないユースケースはあるのか? 移行パスはどのようになるべきか?

参照実装は https://github.com/jasnell/new-streams にあります。

API.md
をご覧ください。サンプルディレクトリには動作コードが揃っています。

問題点や議論、プルリクエストを歓迎します。Web streams の課題に関する経験がある方、またはこのアプローチのギャップを見つけた方はぜひお知らせください。ただし、「新しいオブジェクトを使うべきだ」と主張するわけではありません。現在の Web Streams 状況を超えて第一原理に戻る議論を起こすことが目的です。

同じ日のほかのニュース

一覧に戻る →

2026/02/28 4:08

Googleを辞めたことが、私の生活を実際に良くしてくれました。

## Japanese Translation: 著者は、Google が検索、メール、およびその他のサービスで拡大する支配力がプライバシーとコントロールの問題となり、ユーザーをテック・ジャイアントから離れるよう促していると主張しています。彼らは、2023年5月に追加された「AIオーバービュー」や2026年1月にGmailで導入された生成的 AI など、Google がデータ収集を拡大しつつある証拠として最近の AI 主導機能を指摘しています。筆者は Brave と DuckDuckGo が検索の90%以上を上回っている一方で、Apple は iOS 上で Google をデフォルト検索エンジンに維持するために年間約200億ドルを支払っており、Chrome OS は約70%の使用率で支配していると述べています。これは広告収益が直接支払いを行わないユーザーを製品化する様子を示しています。 著者はプライバシー重視の代替手段を強調しています:メールなら ProtonMail、Fastmail、Tuta(および Mailbox)、動画なら Curiosity Stream、Nebula、Floatplane、Spotify、Dropout ですが、YouTube は Google が所有しているため競合が存在せず例外であると指摘しています。Google を離れた後、著者はデジタル衛生の改善を報告しています:一度きりのサインアップに主要メールアドレスを使用しないことや、より慎重なアカウント作成が増えています。記事は、人々が Google のダークパターンと習慣形成戦術を認識するにつれて、これら代替手段への採用が拡大し、テック業界がより透明でプライバシー尊重型サービスを提供するよう促されるだろうと予測しています。

2026/02/27 23:56

**OpenAI、$730 B のプレマネー評価で 1,10 億ドルを調達**

## Japanese Translation: **要約** OpenAIは、記録的な1,100億ドルのプライベート資金調達ラウンドを閉鎖し、プレマネーバリュエーションを7300億ドルに引き上げました。この取引は、Amazon(500億ドル+AGI実現または年末までにIPOが達成された場合に最大350億ドルのオプション付き)とNvidia(300億ドル)、SoftBank(300億ドル)が主導しています。OpenAIは追加投資家向けにラウンドを開放し続けています。 Amazonの投資には、Bedrock AIサービス上での新しい「ステートフルランタイム環境」、AWSコンピューティング支出の38億ドルから100億ドルへの拡大、少なくとも2GWのTrainium推論ハードウェア使用のコミットメント、およびAmazon消費者製品向けカスタムモデル構築が含まれます。Nvidiaは、Vera Rubinシステムで3GWの専用推論容量と2GWのトレーニングを提供し、正確な資金配分は未公開です。 これは、2025年3月に3000億ドル評価額で400億ドルを調達した以前のラウンド(当時最大のプライベート調達)に続くものです。Amazon CEOのAndy JassyはAIアプリとエージェント開発への協力の影響を強調し、Nvidia CEOのJensen Huangは1月にOpenAIへの大規模サポートを再確認しました。このイベントは2026年6月9日にボストン(MA)でTechCrunchによって報道されました。

2026/02/28 1:33

**NASA、アーテミス計画の全面的な見直しを発表:安全性への懸念と遅延が背景に**

## Japanese Translation: NASAは、アーテミス月面計画を再設計し、統合システムテストのために2027年に専用フライトを追加し、最初の有人着陸を2028年まで延期しました。この改訂により、将来のミッションは単一で低出力の上部段(実質的にExploration Upper Stage (EUS) を廃止)を中心に統合され、Block 1 SLS 構成が継続使用されます。これにより、複数のロケットバリエーションと高い打ち上げガントリーの必要性が排除されました。この改訂は、航空宇宙安全諮問委員会(Aerospace Safety Advisory Panel)の報告書で「過度にリスクがある」とされた元計画を受けており、アーテミス II の水素漏れやヘリウム充圧問題などの技術的障害に対応しています。Apollo 9 の軌道テスト手法を模倣することで、NASA は複雑さを削減しつつ信頼性を確保することを目指しています。 商業パートナーである SpaceX、Blue Origin、Boeing、ULA、および Lockheed Martin は改訂されたスケジュールを受け入れ、SLS ミッションの年次打ち上げペースを支えるようになりました。2027 年にアーテミス III が1つまたは両方のランダーとドッキングして統合テストを行い、その後の 2028 年(アーテミス IV と V)のフライトでは準備が整ったランダーを使用します。NASA は年次ペースを維持するために労働力と技術能力を再構築し、打ち上げリスクとコストを低減する計画です。 この変更は Isaacman の「軌道経済」ビジョンも推進し、空間運用からより多くの価値を引き出して持続可能な月面存在を支援し、永続的な税金負担に依存しない構造を実現します。全体として、新しいアーキテクチャはインフラストラクチャを合理化し、宇宙飛行サービスの新たな商業機会を開きつつ、NASA の長期的探査目標を維持しています。

JavaScriptでより優れたストリームAPIを実装できる可能性があります。 | そっか~ニュース