详解nodejs中的异步迭代器

前言

从 Node.jsv10.0.0 开始,异步迭代器就出现中了,最近它们在社区中的吸引力越来越大。在本文中,我们将讨论异步迭代器的作用,还将解决它们可能用于什么目的的问题。

什么是异步迭代器

那么什么是异步迭代器?它们实际上是以前可用的迭代器的异步版本。当我们不知道迭代的值和最终状态时,可以使用异步迭代器,最终我们得到可以解决{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-of 循环相反,for-await-of 循环将会 等待它收到的每个 promise 解析后再继续执行下一个。

除了流,当前没有太多支持异步迭代的结构,但是可以将符号手动添加到任何可迭代的结构中,如此处所示。

作为异步迭代器流

异步迭代器在处理流时非常有用。可读流、可写流、双工流和转换流都支持异步迭代器。

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);
  }
}

如果以这种方式编写代码,则不必通过迭代来获取每个数据块时监听data和end事件,并且 for-await-of 循环随着流本身结束而结束。

调用有分页功能的 API

你还可以用异步迭代从使用分页的源中轻松获取数据。为此,我们还需要一种从 Node https 请求方法提供给我们的流中重构响应主体的方法。也可以在这里使用异步迭代器,因为 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,并附加 data chunk
          到响应主体的其余部分
        */
        for await (const chunk of res) {
          body += chunk;
        }

        // 处理没有 body 的情况
        if (!body) resolve({});
        // 我们需要解析正文以获取 json,因为它是一个字符串
        const result = JSON.parse(body);
        resolve(result);
      } catch(error) {
        reject(error)
      }
    });

    await req;
    req.end();
  });
}

我们将向Cat API发出请求,以 10 张为一组获取一些猫的图片。我们还将在请求之间添加 7 秒的延迟,最大页面数为5,以避免导致 cat API 过载。

我们还将在请求和最大页数之间添加 7 秒钟的延迟5个以避免猫cat 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;
      // Stop after 5 pages
      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);
      // Wait for 7 seconds between requests
      await new Promise(resolve => setTimeout(resolve, 7000));
    }
  } catch(error) {
    console.log(error);
  }
})()

这样,我们就会每隔 7 秒钟自动取回一整页的猫图片,以供欣赏。

一种更常见的在页面之间导航的方法是实现next和previous方法并将它们公开为控件:

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 v63+、 Firefox v57+ 和 Safari v11.1 中可用。但是当前在 IE 和 Edge 中不可用。

以上就是详解nodejs中的异步迭代器的详细内容,更多关于nodejs异步迭代器的资料请关注我们其它相关文章!

(0)

相关推荐

  • Nodejs探秘之深入理解单线程实现高并发原理

    前言 从Node.js进入我们的视野时,我们所知道的它就由这些关键字组成 事件驱动.非阻塞I/O.高效.轻量,它在官网中也是这么描述自己的. Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. 于是在我们刚接触Node

  • NodeJs内存占用过高的排查实战记录

    前言 一次线上容器扩容引发的排查,虽然最后查出并不是真正的 OOM 引起的,但还是总结记录一下其中的排查过程,整个过程像是破案,一步步寻找蛛丝马迹,一步步验证出结果. 做这件事的意义和必要性个人觉得有这么几个方面吧: 从程序员角度讲:追求代码极致,不放过问题,务必保证业务的稳定性这几个方面 从资源角度讲:就是为了降低无意义的资源开销 从公司角度讲:降低服务器成本,给公司省钱 环境:腾讯 Taf 平台上运行的 NodeJs 服务. 问题起因 最开始是因为一个定时功能上线后,线上的容器自动进行了扩容

  • 详解nodejs内置模块

    概述 nodejs内置模块指的是除默认提供的语法之外,提供的美容,无需下载,直接引入,引入只写名称即可. nodejs内置模块: 1.path模块  用于处理文件路径. path.normalize(路径解析,得到规范路径): path.join(路径合并): path.resolve(获取绝对路径): path.relative(获取相对路径). ...... 2.until模块  弥补js功能不足,新增API. util.format(格式化输出字符串); util.isArray(检查是否

  • 如何用nodejs给C#写一个数据表的实体类生成工具

    虽然微软提供了T4模板,但是我感觉非常难用.哪儿比得上直接用脚本来写模板来的爽. 因为要给一个老项目做周边的工具,需要连接到数据库. 我习惯性用EntityFrameworkCore来做,因为毕竟从出道开始就一直在用的一个ORM. EF6时代,vs提供了dbfirst,但是只是针对sqlserver好像. 因为这次的数据库是MySQL,所以vs很多东西都支持不够了. 但是支持不够就自己动手丰衣足食嘛. 我们使用ejs这个模板引擎来做生成器. npm install ejs 然后用查询出表结构:

  • 详解使用Nodejs内置加密模块实现对等加密与解密

    加密与解密是保证通讯安全的一种重要手段,现在加密算法已经有很多,并且都有成熟的软件包可以使用,这就大大降低了应用开发程序员的负担,只需要使用这些第三方提供的加密解密库就可以使用了,在Node.js中其实提供了一个非常强大而且方便的加密与解密模块crypto,我们不需要使用第三方的NPM库就能实现简单的加密与解密功能,毕竟使用加密与解密的目的就是为了保证通讯的安全,而使用非官方的第三方库总是有可能存在添加的后门或者什么的,而使用Node.js自带的crypto模块就能最大程度的保证加密的安全性.

  • 浅谈JS和Nodejs中的事件驱动

    事件驱动和发布-订阅 事件驱动架构是建立在软件开发中一种通用模式上的,这种模式被称为发布-订阅或观察者模式. 在事件驱动架构中,至少有两个参与者:主题(subject)和观察者(observer). 主题就像调频收音机一样,向有兴趣收听该主题所说内容的观察者进行广播. 观察者可能只有一个,也可能有一百个,这都没有关系,只要主题有一些要广播的消息就够了. 请记住,事件驱动.发布-订阅和观察者模式在实践中不是一回事,但在理想情况下,它们使用相同的方法:一个实体广播一条消息,其他实体侦听该消息. 发布

  • nodejs模块系统源码分析

    概述 Node.js的出现使得前端工程师可以跨端工作在服务器上,当然,一个新的运行环境的诞生亦会带来新的模块.功能.抑或是思想上的革新,本文将带领读者领略 Node.js(以下简称 Node) 的模块设计思想以及剖析部分核心源码实现. CommonJS 规范 Node 最初遵循 CommonJS 规范来实现自己的模块系统,同时做了一部分区别于规范的定制.CommonJS 规范是为了解决JavaScript的作用域问题而定义的模块形式,它可以使每个模块在它自身的命名空间中执行. 该规范强调模块必须

  • nodejs的错误处理过程记录

    本文以连接错误ECONNREFUSED为例,看看nodejs对错误处理的过程. 假设我们有以下代码 1. const net = require('net'); 2. net.connect({port: 9999}) 如果本机上没有监听9999端口,那么我们会得到以下输出. 1. events.js:170 2. throw er; // Unhandled 'error' event 3. ^ 4. 5. Error: connect ECONNREFUSED 127.0.0.1:9999

  • 如何在Nodejs中使用模块fs文件系统

    概述 node 的fs文档密密麻麻的 api 非常多,毕竟全面支持对文件系统的操作.文档组织的很好,操作基本分为文件操作.目录操作.文件信息.流这个大方面,编程方式也支持同步.异步和 Promise. 本文记录了几个文档中没详细描写的问题,可以更好地串联fs文档思路: 文件描述符 同步.异步与 Promise 目录与目录项 文件信息 stream 文件描述符 文件描述符是一个非负整数.它是一个索引值,操作系统可以根据它来找到对应的文件. 在 fs 的很多底层 api 中,需要用到文件描述符.在文

  • nodejs代码执行绕过的一些技巧汇总

    在php中,eval代码执行是一个已经被玩烂了的话题,各种奇技淫巧用在php代码执行中来实现bypass.这篇文章主要讲一下nodejs中bypass的一些思路. 1. child_process 首先介绍一下nodejs中用来执行系统命令的模块child_process.Nodejs通过使用child_process模块来生成多个子进程来处理其他事物.在child_process中有七个方法它们分别为:execFileSync.spawnSync,execSync.fork.exec.exec

随机推荐