You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ReadableStreamDefaultReader.read() Promise拒绝后,何时可安全再次调用?

ReadableStreamDefaultReader.read() 拒绝后可继续读取的场景分析

Great question! You're spot-on about the common case: when reader.read() rejects (like with a fetch network failure), the underlying stream almost always enters an errored state. Any subsequent calls to read() will just re-throw the same error, so continuing to read is unsafe and pointless.

But there are edge cases—specifically with custom ReadableStream implementations—where you can recover and keep reading after a rejection. Let's break them down:

  • Custom streams with recoverable pull errors
    If you've built your own ReadableStream using an underlyingSource, you can design the pull() method to handle temporary errors without marking the entire stream as errored. Here's the key: don't call controller.error() for errors you want to recover from. Instead, handle the error internally and let the next pull() attempt proceed.

    For example, imagine a stream that fetches chunks from a flaky API:

    const flakyStream = new ReadableStream({
      async pull(controller) {
        try {
          const response = await fetch('/flaky-api/chunk');
          const chunk = await response.text();
          controller.enqueue(chunk);
        } catch (err) {
          // Only mark the stream as errored for permanent failures
          if (err.status === 500 || err.type === 'PERMANENT') {
            controller.error(err);
            return;
          }
          // For temporary issues (e.g., 503, network blip), just skip this pull
          console.log('Temporary failure, will retry on next read');
        }
      }
    });
    
    const reader = flakyStream.getReader();
    async function readLoop() {
      while (true) {
        try {
          const { done, value } = await reader.read();
          if (done) break;
          processChunk(value);
        } catch (err) {
          // Check if the error is recoverable
          if (err.status === 503 || err.type === 'TEMPORARY') {
            console.log('Recoverable error, continuing read loop');
            continue;
          }
          // Fatal error: exit the loop
          console.error('Unrecoverable stream error:', err);
          break;
        }
      }
    }
    

    In this case, a temporary failure in pull() causes the current reader.read() to reject, but the stream itself stays in a valid state. The next read() call will trigger a new pull() attempt, allowing you to keep reading.

  • Releasing the lock and re-acquiring a reader (for restartable streams)
    If your custom stream is designed to be restartable (e.g., it can reset its internal state after an error), you can release the lock on the errored reader and get a new one. This only works if the stream hasn't been permanently closed or errored by the underlying source.

    For example:

    async function restartRead(stream) {
      let reader = stream.getReader();
      try {
        const { done, value } = await reader.read();
        // Process value...
      } catch (err) {
        // If recoverable, release the lock and get a new reader
        if (err.type === 'RESTARTABLE') {
          reader.releaseLock();
          reader = stream.getReader();
          // Continue reading with the new reader
          await restartRead(stream);
        }
      }
    }
    

    Note that this is a niche use case—most streams (including all native browser streams like fetch responses) don't support restarting after an error.

Critical Caveat for Native Streams

It's important to emphasize: none of these recovery scenarios apply to native streams like fetch responses or FileReader streams. These streams are one-time, immutable, and any error (like a network failure) puts the stream into a permanent errored state. Any subsequent read() calls will immediately reject with the same error, so you must stop reading entirely.

In short: recovery after a reader.read() rejection is only possible if you've explicitly designed your custom ReadableStream to support it. For all out-of-the-box streams, a rejection means the stream is dead, and you should clean up immediately.

内容的提问来源于stack exchange,提问作者thurt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:36:59