Learn web development

使用 async 进行非同步流控制

有些本地图书馆网页的控制器代码,会依赖多重非同步要求的结果,可能会需要以某种特定次序运行,或者以平行方式运行。为了管理流控制,并在我们所有需要用到的信息,都已经可以取用的时候,再绘制网页,我们将使用许多人采用的 node async 模组。

注意: 在 JavaScript 中有许多其他方法,可以管理异步行为和流控制,包括相对较新的 JavaScript 语言功能,如 Promises

Async 有很多有用的方法(请查看文档)。一些最重要的功能是:

为什么需要这么做?

我们在 Express 中使用的大多数方法,都是异步的 - 您指定要执行的操作,传递回调。该方法立即返回,并在请求的操作完成时,调用回调。按照 Express 中的惯例,回调函数将错误值作为第一个参数传递(或成功时为 null),并将函数的结果(如果有的话)作为第二个参数传递。

如果控制器只需要执行一个异步操作,来获取呈现页面所需的信息,那么实现很简单 - 我们只需在回调中呈现模板。下面的代码片段,显示了一个函数,该函数呈现模型 SomeModel 的计数(使用Mongoose count()方法):

exports.some_model_count = function(req, res, next) {
 
  SomeModel.count({ a_model_field: 'match_value' }, function (err, count) {
    // ... do something if there is an err

    // On success, render the result by passing count into the render function (here, as the variable 'data').
    res.render('the_template', { data: count } );
  });
}

但是,如果您需要进行多个异步查询,并且在完成所有操作之前,无法呈现页面,该怎么办?一个单纯的实现可以用 “菊花链” 连接请求,在先前请求的回调中,启动后续请求,并在最终回调中呈现响应。这种方法的问题,是我们的请求必须串行运行,即使并行运行它们可能更有效。这也可能导致复杂的嵌套代码,通常称为回调地狱

一个更好的解决方案,是并行执行所有请求,然后在所有查询完成后执行单个回调。这是 Async 模块简化的流操作!

平行的非同步操作

方法async.parallel()用于并行运行多个异步操作。

async.parallel() 的第一个参数,是要运行的异步函数的集合(数组,对象或其他可迭代的)。每个函数都传递一个回调函数callback(err, result) ,它必须在完成时调用错误err(可以为null)和可选的结果值。

async.parallel()的可选第二个参数是一个回调,它将在第一个参数中的所有函数完成时运行。回调的调用,是使用错误参数和包含各个异步操作结果的结果集合。结果集合与第一个参数的类型相同(即,如果传递异步函数数组,则将使用结果数组,调用最终回调)。如果任何并行函数报告错误,则提前调用回调(具有错误值)。

下面的示例,显示了当我们将对象作为第一个参数传递时它是如何工作的。如您所见,结果将返回到一个对象中,该对象具有与传入的原始函数相同的属性名称。

async.parallel({ 
  one: function(callback) { ... },
  two: function(callback) { ... },
  ...
  something_else: function(callback) { ... }
  }, 
  // optional callback
  function(err, results) {
    // 'results' is now equal to: {one: 1, two: 2, ..., something_else: some_value}
  }
);

如果您将一组函数,作为第一个参数传递,则结果将是一个数组(数组顺序结果,将与声明函数的原始顺序匹配 - 而不是它们完成的顺序)。

序列的非同步操作

async.series()方法用于按顺序运行多个异步操作,后续函数不依赖于先前函数的输出。它本质上是声明的,并且行为与async.parallel().相同。

async.series({ 
  one: function(callback) { ... },
  two: function(callback) { ... },
  ...
  something_else: function(callback) { ... }
  }, 
  // optional callback after the last asynchronous function completes.
  function(err, results) {
    // 'results' is now equals to: {one: 1, two: 2, ..., something_else: some_value} 
  }
);

注意: ECMAScript(JavaScript)语言规范指出,对象的枚举顺序是未定义的,因此可能不会按照在所有平台上指定它们的顺序,调用这些函数。如果顺序真的很重要,那么你应该传递一个数组而不是一个对象,如下所示。

async.series([
  function(callback) {
    // do some stuff ...
    callback(null, 'one'); 
  },
  function(callback) {
    // do some more stuff ... 
    callback(null, 'two'); 
  } 
 ], 
  // optional callback
  function(err, results) {
  // results is now equal to ['one', 'two'] 
  }
);

依赖序列的非同步操作

方法async.waterfall()用于在每个操作依赖于前一个操作的结果时,依次运行多个异步操作。

每个异步函数调用的回调,包含第一个参数的null,与后续参数里的结果。该序列中的每个函数,都将前一个回调的结果参数,作为第一个参数,然后是回调函数。

完成所有操作后,将使用上一操作的结果,调用最终回调。当您参考下面的代码片段时,这种工作方式会更加明确(此示例来自 async 文档):

async.waterfall([
  function(callback) {
    callback(null, 'one', 'two'); 
  }, 
  function(arg1, arg2, callback) { 
    // arg1 now equals 'one' and arg2 now equals 'two' 
    callback(null, 'three'); 
  }, 
  function(arg1, callback) {
    // arg1 now equals 'three'
    callback(null, 'done');
  }
], function (err, result) {
  // result now equals 'done'
}
);

安装 async

使用 NPM 包管理器安装 async 模块,以便我们可以在代码中使用它。您可以常规方式执行此操作,在 LocalLibrary 项目的根目录中,打开命令提示并输入以下命令:

 

npm install async

下一步

文档标签和贡献者

此页面的贡献者: edgar-chen
最后编辑者: edgar-chen,