Node.js中常见的异步/等待设计模式

Node.js中常见的异步/等待设计模式

Node.js中的异步/等待打开了一系列强大的设计模式。现在可以使用基本语句和循环来完成过去采用复杂库或复杂承诺链接的任务。我已经用co编写了这些设计模式,但异步/等待使得这些模式可以在vanilla Node.js中访问,不需要外部库。iffor

Node.js中常见的异步/等待设计模式

重试失败的请求

其强大之await处在于它可以让你使用同步语言结构编写异步代码。例如,下面介绍如何使用回调函数使用superagent HTTP库重试失败的HTTP请求。

const superagent = require('superagent');

const NUM_RETRIES = 3;

request('http://google.com/this-throws-an-error', function(error, res) {
  console.log(error.message); // "Not Found"
});

function request(url, callback) {
  _request(url, 0, callback);
}

function _request(url, retriedCount, callback) {
  superagent.get(url).end(function(error, res) {
    if (error) {
      if (retriedCount >= NUM_RETRIES) {
        return callback && callback(error);
      }
      return _request(url, retriedCount + 1, callback);
    }
    callback(res);
  });
}

不是太难,但涉及递归,对于初学者来说可能非常棘手。另外,还有一个更微妙的问题。如果superagent.get().end()抛出一个同步异常会发生什么?我们需要将这个_request()调用包装在try / catch中以处理所有异常。必须在任何地方这样做都很麻烦并且容易出错。随着异步/ AWAIT,你可以写只用同等功能fortry/catch

const superagent = require('superagent');

const NUM_RETRIES = 3;

test();

async function test() {
  let i;
  for (i = 0; i < NUM_RETRIES; ++i) {
    try {
      await superagent.get('http://google.com/this-throws-an-error');
      break;
    } catch(err) {}
  }
  console.log(i); // 3
}

相信我,这是有效的。我记得我第一次尝试这种模式与合作,我感到莫名其妙,它实际工作。但是,下面的就不能正常工作。请记住,await必须始终在async函数中,而传递给forEach()下面的闭包不是async

const superagent = require('superagent');

const NUM_RETRIES = 3;

test();

async function test() {
  let arr = new Array(NUM_RETRIES).map(() => null);
  arr.forEach(() => {
    try {
      // SyntaxError: Unexpected identifier. This `await` is not in an async function!
      await superagent.get('http://google.com/this-throws-an-error');
    } catch(err) {}
  });
}

处理MongoDB游标

MongoDB的find()函数返回一个游标。游标基本上是一个具有异步next()函数的对象,它可以获取查询结果中的下一个文档。如果没有更多结果,则next()解析为空。MongoDB游标有几个辅助函数,如each(),,map()toArray(),猫鼬ODM增加了一个额外的eachAsync()函数,但它们都只是语法上的糖next()

没有异步/等待,next()手动调用涉及与重试示例相同的递归类型。使用async / await,你会发现自己不再使用助手函数(除了可能toArray()),因为用循环遍历游标for要容易得多:

const mongodb = require('mongodb');

test();

async function test() {
  const db = await mongodb.MongoClient.connect('mongodb://localhost:27017/test');

  await db.collection('Movies').drop();
  await db.collection('Movies').insertMany([
    { name: 'Enter the Dragon' },
    { name: 'Ip Man' },
    { name: 'Kickboxer' }
  ]);

  // Don't `await`, instead get a cursor
  const cursor = db.collection('Movies').find();
  // Use `next()` and `await` to exhaust the cursor
  for (let doc = await cursor.next(); doc != null; doc = await cursor.next()) {
    console.log(doc.name);
  }
}

如果这对你来说不够方便,有一个TC39的异步迭代器建议可以让你做这样的事情。请注意,下面的代码并没有在Node.js的任何目前发布的版本工作,这只是什么是可能在未来的一个例子。

const cursor = db.collection('Movies').find().map(value => ({
  value,
  done: !value
}));

for await (const doc of cursor) {
  console.log(doc.name);
}

并行多个请求

上述两种模式都按顺序执行请求,只有一个next()函数调用在任何给定的时间执行。怎么样并行多个异步任务?让我们假装你是一个恶意的黑客,并且想要与bcrypt并行地散列多个明文密码。

const bcrypt = require('bcrypt');

const NUM_SALT_ROUNDS = 8;

test();

async function test() {
  const pws = ['password', 'password1', 'passw0rd'];

  // `promises` is an array of promises, because `bcrypt.hash()` returns a
  // promise if no callback is supplied.
  const promises = pws.map(pw => bcrypt.hash(pw, NUM_SALT_ROUNDS));

  /** * Prints hashed passwords, for example: * [ '$2a$08$nUmCaLsQ9rUaGHIiQgFpAOkE2QPrn1Pyx02s4s8HC2zlh7E.o9wxC', * '$2a$08$wdktZmCtsGrorU1mFWvJIOx3A0fbT7yJktRsRfNXa9HLGHOZ8GRjS', * '$2a$08$VCdMy8NSwC8r9ip8eKI1QuBd9wSxPnZoZBw8b1QskK77tL2gxrUk.' ] */
  console.log(await Promise.all(promises));
}

Promise.all()函数接受一组承诺,并返回一个承诺,等待数组中的每个承诺解析,然后解析为一个数组,该数组包含解析的原始数组中每个承诺的值。每个bcrypt.hash()调用都会返回一个promise,所以promises在上面的数组中包含一组promise,并且value的值await Promise.all(promises)是每个bcrypt.hash()调用的结果。

Promise.all()并不是您可以并行处理多个异步函数的唯一方式,还有一个Promise.race()函数可以并行执行多个promise,等待第一个解决的承诺并返回承诺解决的值。以下是使用Promise.race()async / await 的示例:

/** * Prints below: * waited 250 * resolved to 250 * waited 500 * waited 1000 */
test();

async function test() {
  const promises = [250, 500, 1000].map(ms => wait(ms));
  console.log('resolved to', await Promise.race(promises));
}

async function wait(ms) {
  await new Promise(resolve => setTimeout(() => resolve(), ms));
  console.log('waited', ms);
  return ms;
}

请注意,尽管Promise.race()在第一个承诺解决后解决,但其余的async功能仍然继续执行。请记住,承诺不可取消。

继续

异步/等待是JavaScript的巨大胜利。使用这两个简单的关键字,您可以从代码库中删除大量外部依赖项和数百行代码。您可以添加强大的错误处理,重试和并行处理,只需一些简单的内置语言结构。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/101598.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Java设计模式之迭代子模式

    本文继续介绍23种设计模式系列之观察者模式。定义在软件构建过程中,集合对象内部结构常常变化各异,但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种“透明遍历”也为同一种算法在多种集合对象上进行操作提供了可能。使用面向对象技术将这种遍历机制抽象为“迭代器对象”为“应对变化中的集合对象”提供了一种优雅的方式。迭代子(Iterator)模式又叫游标

    2022年3月11日
    35
  • 十九、原型模式—通过复制生产实例 #和设计模式一起旅行#[通俗易懂]

    拔一根猴毛,变出十万个孙悟空故事背景在java中,我们一般创建实例的时候可以使用了new 关键词指定类名来生成类的实例。例如:Preson p = new Person();或者Person p = Person.getInstance();但是是在实际开发过程中,有时候会有“不指定类名的前提下生成实例”的需求。这种情况下,就不能通过上面的这种方式生成实例!…

    2022年2月27日
    38
  • 设计模式、框架、架构、平台的区别「建议收藏」

    设计模式、框架、架构、平台的区别「建议收藏」区分什么是架构、框架、模式和平台,一直都感觉这几个词过于抽象和模糊,今天大家来说说到底什么是架构、框架、模式和平台? 收集了的一些来自网上各自的定义和区分如下: 设计模式 1、设计模式为什么要先说设计模式?因为设计模式在这些概念中是最基本的,而且也比较简单。那么什么是设计模式呢?说的直白点,设计模式就是告诉你针对特定问题如何组织类、对象和接口之间的关系,是前人总结的经验

    2022年10月10日
    0
  • .NET设计模式(11):组合模式(Composite Pattern)

    .NET设计模式(11):组合模式(Composite Pattern)

    2021年7月22日
    64
  • 什么是Web 2.0——下一代软件的设计模式和商业模式 (全文翻译—1 博客版序)

    什么是Web 2.0——下一代软件的设计模式和商业模式 (全文翻译—1 博客版序)这篇文章,是经O’Reilly公司的TimO’Reilly先生授权后,进行全文翻译、出版的。包括:杂志节选版和网络全文版。转载:请遵循CC版权,保留原文作者和翻译作者信息,以及文章链接。感谢:互联网周刊的李洋提供了许多建议。几个文章版本链接:英文原文:http://www.oreillynet.com/pub/a/oreilly/tim/news/2005/09/30/

    2022年7月21日
    11
  • 十三、外观模式—— 简化接口 #和设计模式一起旅行#

    我不想成为上帝或英雄,只想成为一棵树,为岁月而生长,不伤害任何人。 ——米沃什故事背景在英国体验了康桥的魅力,我挥一挥衣袖,不带走一片云彩,但是 英国的天空没有留下我的痕迹,但我曾去过。哈哈!从英国到法国,在浪漫的巴黎,我和设计模式MM感受到这个城市别样的风景,很是吸引人,我们决定在这里待一段时间在走。于是去政府部门办理一些手续,本来以为会花费很多时间的,因为之前办…

    2022年2月27日
    40

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号