最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • [译] 关于 Node.js 中的异步迭代器

    正文概述 掘金(Isildur46)   2021-03-01   776

    关于 Node.js 中的异步迭代器

    Node 在 10.0.0 版本中加入了异步迭代器,并且这个功能在最近逐渐赢得了社区的青睐。本文我们将了解什么是异步迭代器,并探索它的使用场景。

    什么是异步迭代器

    所以什么是异步迭代器?他们其实就是之前迭代器的异步版本。当我们在迭代过程中不清楚值和结束状态时,我们可以使用异步迭代器,用它来解决(resolve)普通的 { value: any, done: boolean } 对象并最终得到 promise。我们也可以使用 for-await-of 循环来帮助我们在异步迭代器中进行循环操作,这就像同步迭代器的 for-of 循环那样。

    const asyncIterable = [1, 2, 3];
    asyncIterable[Symbol.asyncIterator] = async function*() {
      for (let i = 0; i < asyncIterable.length; i++) {
        yield { value: asyncIterable[i], done: false }
      }
      yield { done: true };
    };
    
    (async function() {
      for await (const part of asyncIterable) {
        console.log(part);
      }
    })();
    
    

    for-await-of 循环会等待每个它接收的 promise 被解决,然后再执行下一个,这和常规的 for-of 循环是对应的。

    目前,除了流以外并没有很多结构支持异步迭代器。但是正如本例所示,可以手动在任何可迭代对象上添加 symbol。

    作为异步迭代器的流

    在处理流的时候,异步迭代器非常有用。可读流、可写流、双向流、转换流都带有开箱即用的 asyncIterator symbol。

    async function printFileToConsole(path) {
      try {
        const readStream = fs.createReadStream(path, { encoding: 'utf-8' });
    
        for await (const chunk of readStream) {
          console.log(chunk);
        }
    
        console.log('EOF');
      } catch(error) {
        console.log(error);
      }
    }
    
    

    如果你像这样编写代码,就不必在迭代每个分片时去监听 dataend 事件了,并且 for-await-of 循环会在流结束时自行终止。

    消费分页的 API

    我们也可以借助异步迭代器,从而让我们很容易地从源获取分页过的数据。为此,我们需要某种方式来重构 Node https 请求方法所返回的流的响应体。由于 Node 中请求和响应都是流,我们也可以使用异步迭代器实现这一功能:

    const https = require('https');
    
    function homebrewFetch(url) {
      return new Promise(async (resolve, reject) => {
        const req = https.get(url, async function(res) {
          if (res.statusCode >= 400) {
            return reject(new Error(`HTTP Status: ${res.statusCode}`));
          }
    
          try {
            let body = '';
    
            /*
              不再使用 res.on 监听流的数据,
              而是使用 for-await-of 向剩余响应体
              拼接数据切片
            */
            for await (const chunk of res) {
              body += chunk;
            }
        
            // 处理没有响应体的情况
            if (!body) resolve({});
            // 我们需要解析 body 来获取 json,它是字符串
            const result = JSON.parse(body);
            resolve(result);
          } catch(error) {
            reject(error)
          }
        });
    
        await req;
        req.end();
      });
    }
    
    

    我们会向 猫猫 API 发起请求,获取一些猫猫图,10 张一页,每个请求中间暂停 7 秒,最多获取 5 页数据,这样就能避免猫猫 API 过载而出现猫病。

    function fetchCatPics({ limit, page, done }) {
      return homebrewFetch(`https://api.thecatapi.com/v1/images/search?limit=${limit}&page=${page}&order=DESC`)
        .then(body => ({ value: body, done }));
    }
    
    function catPics({ limit }) {
      return {
        [Symbol.asyncIterator]: async function*() {
          let currentPage = 0;
          // 5 页之后停止
          while(currentPage < 5) {
            try {
              const cats = await fetchCatPics({ currentPage, limit, done: false });
              console.log(`Fetched ${limit} cats`);
              yield cats;
              currentPage ++;
            } catch(error) {
              console.log('There has been an error fetching all the cats!');
              console.log(error);
            }
          }
        }
      };
    }
    
    (async function() {
      try {
        for await (let catPicPage of catPics({ limit: 10 })) {
          console.log(catPicPage);
          // 每个请求间等待 7 秒
          await new Promise(resolve => setTimeout(resolve, 7000));
        }
      } catch(error) {
        console.log(error);
      }
    })()
    
    

    这样一来,我们可以每 7 秒自动获取一整页的猫猫图,然后吸爆。

    有一种更常规的翻页手法是实现并暴露 nextprevious 两个方法,用它们来控制页面导航:

    function actualCatPics({ limit }) {
      return {
        [Symbol.asyncIterator]: () => {
          let page = 0;
          return {
            next: function() {
              page++;
              return fetchCatPics({ page, limit, done: false });
            },
            previous: function() {
              if (page > 0) {
                page--;
                return fetchCatPics({ page, limit, done: false });
              }
              return fetchCatPics({ page: 0, limit, done: true });
            }
          }
        }
      };
    }
    
    try {
        const someCatPics = actualCatPics({ limit: 5 });
        const { next, previous } = someCatPics[Symbol.asyncIterator]();
        next().then(console.log);
        next().then(console.log);
        previous().then(console.log);
    } catch(error) {
      console.log(error);
    }
    
    

    如你所见,当我们有很多页数据要获取,或者类似于在应用的 UI 中要实现无限滚动时,异步迭代器是非常有用的。

    浏览器支持这些功能已经有一段时间了,Chrome 从 63 版开始支持,Firefox 从 57 版开始支持,Safari 从 11.1 版开始支持。然而 IE 和 Edge 目前并不支持(译注:Edge 已从 79 版本开始支持了)。

    关于异步迭代器的使用场景,你有新点子了吗?你是否已准备好了在实际应用中使用它?

    请在下方评论让我们一起交流吧!



    起源地下载网 » [译] 关于 Node.js 中的异步迭代器

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元