stream 数据流

除了返回Promise对象,Fetch API 还有一个特点,就是数据传送是以数据流(stream)的形式进行的。对于大文件,数据是一段一段得到的。

  1. response.text().then(function (responseText) {
  2. console.log(responseText);
  3. }

上面代码中,text()就是一个数据流读取器。

Fetch API 提供以下五个数据流读取器。

  • .text():返回字符串
  • .json():返回JSON对象
  • .formData():返回FormData对象
  • .blob():返回blob对象
  • .arrayBuffer():返回二进制数组ArrayBuffer对象

数据流只能读取一次,一旦读取,数据流就空了。再次读取就不会得到结果。解决方法是在读取之前,先使用.clone()方法,复制一份一模一样的副本。

  1. var url = 'LargeFile.txt';
  2. var progress = 0;
  3. var contentLength = 0;
  4. fetch(url).then(function (response) {
  5. // 本次请求总的数据长度
  6. contentLength = response.headers.get('Content-Length');
  7. var getStream = function (reader) {
  8. // ...
  9. };
  10. return getStream(response.body.getReader());
  11. })
  12. .catch(function (error) {
  13. console.log(error);
  14. });

上面代码中,response.body.getReader()返回的就是数据流之中的一段。处理数据流的getStream函数代码如下。

  1. var progress = 0;
  2. var contentLength = 0;
  3. var getStream = function (reader) {
  4. return reader.read().then(function (result) {
  5. // 如果数据已经读取完毕,直接返回
  6. if (result.done) {
  7. return;
  8. }
  9. // 取出本段数据(二进制格式)
  10. var chunk = result.value;
  11. var text = '';
  12. // 假定数据是UTF-8编码,前三字节是数据头,
  13. // 而且每个字符占据一个字节(即都为英文字符)
  14. for (var i = 3; i < chunk.byteLength; i++) {
  15. text += String.fromCharCode(chunk[i]);
  16. }
  17. // 将本段数据追加到网页之中
  18. document.getElementById('content').innerHTML += text;
  19. // 计算当前进度
  20. progress += chunk.byteLength;
  21. console.log(((progress / contentLength) * 100) + '%');
  22. // 递归处理下一段数据
  23. return getStream(reader);
  24. };
  25. };

上面这样的数据流处理,可以提高网站性能表现,减少内存占用,对于请求大文件或者网速慢的场景相当有用。传统的XMLHTTPRequest对象不支持数据流,所有的数据必须放在缓存里,等到全部拿到后,再一次性吐出来。