在浏览器中使用JavaScript以等待方式读取流

时间:2019-07-11 14:30:47

标签: javascript

我正在使用以下代码,使用带有进度指示的fetch在浏览器中根据用户手势下载较大的内容:

const url = 'https://source.unsplash.com/random';
const response = await fetch(url);
const total = Number(response.headers.get('content-length'));
let loaded = 0;
const reader = response.body.getReader();
let result;
while (!(result = await reader.read()).done) {
  loaded += result.value.length;
  // Display loaded/total in the UI
}

我在related question中看到了一个片段,这使我相信可以将其简化为:

const url = 'https://source.unsplash.com/random';
const response = await fetch(url);
const total = Number(response.headers.get('content-length'));
let loaded = 0;
for await (const result of response.body.getReader()) {
  loaded += result.value.length;
  // Display loaded/total in the UI
}

getReader返回一个ReadableStreamDefaultReader,它来自Streams API,这是一个Web API以及一个Node API,这使得仅查找与Web相关的信息真的很困难。

在上面的代码段中,代码失败,并显示 response.body.getReader(...)不是函数或返回值不可异步迭代。我检查了对象原型,确实不认为它上面有Symbol.asyncIterator,所以,难怪浏览器无法对其进行迭代。

因此该问题中的代码一定是错误的,但是现在我想知道, 是否可以采用这样的流并使用for-await对其进行迭代?我想您需要将其包装在异步生成器中,并以与第一个代码段相似的方式产生块,对吧?

是否有一个现有的或计划中的API使它更加简化,更接近第二个片段,但实际上起作用了?

2 个答案:

答案 0 :(得分:1)

ReadableStream本身implements an async iterable

 for await (const result of response.body) {
   loaded += result.value.length;
   // Display loaded/total in the UI
 }

答案 1 :(得分:0)

response.body.getReader() .read()返回的{value:..., done: Boolean}完全符合asyncIterator的需求;在ReadableStream本身获得asyncIterator实现之前,您可以轻松地对其进行填充:

if (!response.body[Symbol.asyncIterator]) {
  response.body[Symbol.asyncIterator] = () => {
    const reader = response.body.getReader();
    return {
      next: () => reader.read(),
    };
  };
}
for await (const result of response.body) {
  loaded += result.length;
  console.log(((loaded / total) * 100).toFixed(2), '%');
}

请参见https://jsfiddle.net/6ostwkr2/