You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

iOS环境下JavaScript Fetch Stream无法通过`for await...of`迭代读取的异常问题

iOS Safari中ReadableStream异步迭代失败的解决方案

你观察得非常准确——iOS上的WebKit内核浏览器(包括Safari以及所有iOS上的第三方浏览器,因为iOS强制所有浏览器使用WebKit)的ReadableStream确实没有实现异步迭代器接口,这就是for await (const byteArray of stream)抛出TypeError的核心原因。

差异背后的原因

从你贴出的两个环境的ReadableStream原型对比能清晰看到:

  • Windows下的Chromium内核浏览器(Chrome/Edge等)的ReadableStream原型上有values()方法,并且实现了Symbol.asyncIterator,这是for await...of语法能正常工作的前提;
  • 而iOS WebKit的ReadableStream只实现了标准中最基础的方法(getReader()cancel()等),还未添加异步迭代的支持,这是WebKit团队的实现进度问题,目前稳定版还未纳入该特性。

兼容所有环境的流读取方案

既然异步迭代用不了,我们可以用ReadableStream的基础API——getReader()来手动读取流,这是所有支持ReadableStream的环境都兼容的方案:

async function processStream(stream) {
  // 获取流的默认读取器
  const reader = stream.getReader();
  
  try {
    // 循环读取直到流结束
    while (true) {
      const { done, value } = await reader.read();
      // 流读取完成时退出循环
      if (done) {
        console.log('Stream reading completed');
        break;
      }
      // 处理每一段读取到的byteArray(value就是你之前用byteArray接收的内容)
      console.log('Received chunk:', value);
      // 这里写你的业务逻辑,比如拼接数据、解析内容等
    }
  } catch (err) {
    console.error('Error reading stream:', err);
  } finally {
    // 无论成功还是失败,都释放读取器的锁
    reader.releaseLock();
  }
}

代码细节说明

  1. getReader()会返回一个ReadableStreamDefaultReader,它的read()方法返回Promise,每次resolve会得到包含donevalue的对象:
    • done:布尔值,为true时表示流已经全部读取完毕;
    • value:当前读取到的二进制数据块(也就是你之前用byteArray接收的内容);
  2. finally块中的releaseLock()很关键,它会释放读取器对当前流的锁定,让流可以被其他操作复用;
  3. 这个方法完全遵循WHATWG Stream标准,在iOS Safari、Windows浏览器、Android浏览器等所有支持ReadableStream的环境都能正常运行。

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

火山引擎 最新活动