在执行所有当前调度的回调后调用函数(节点)

nau*_*boy 5 javascript events callback node.js

我正在使用流式multipart/form-data解析器来处理文件和字段上传.每当解析一个新的字段或文件时,就会触发一个事件并执行一些处理.解析'close'完所有字段和文件后,会触发一个事件,然后我会为请求调用终止函数.这是设置.

parser.on('fields',function(){

    handleField(callback);

});


parser.on('close',function(){
    dismiss();
})
Run Code Online (Sandbox Code Playgroud)

问题是处理字段可能需要一些时间,这使得'close'侦听器在我有机会从字段处理侦听器调用回调之前解除请求.

我试图使用setImmediate根据其描述的功能

在I/O和计时器回调之后,在事件队列中对其回调进行排队.

为了在dismiss()执行所有当前计划的回调后调用我的函数,但是这不起作用,我甚至尝试过process.nextTick()以防止订单被撤销,但没有这样的运气.

所以问题是,dismiss()只有在调用了处理函数的所有当前调度回调之后,我才能调用我的函数?

Sha*_*ank 2

使用PromisesPromise.all,您可以dismiss根据回调的包装函数解决的所有 Promise 的解析来进行未来的操作。

技巧是将回调包装到另一个函数中,该函数在异步(或同步)操作调用回调后解析或拒绝包装器 Promise。您可以将所有 Promise 存储在一个数组中,然后调用Promise.all该数组以生成另一个 Promise .then()

在此示例中,我将假设callback是一个 Node I/O 样式处理程序(即第一个参数是err)并且handleField是一个最终调用 的异步 Node I/O 样式操作callback。从语句“每当解析新字段或文件时,就会触发一个事件,并执行一些处理。” ,我还假设“字段”是一个发生 N 次的事件,其中 N 是字段的数量,因此必须完成 N 个回调才能正确dismiss()。如果我的假设不正确,请随时发表评论。

var promises = [];

parser.on('fields', function() {
  promises.push(
    new Promise(function(resolve, reject) {

      // wrap the original callback into another function.
      // this function either resolves or rejects the Promise.
      handleField(function(err) {
        // if there's an error,
        // pass it to reject instead of throwing it.
        if (err) {
          reject(err);
        } else {
          // calls your callback with given args.
          // resolves Promise with return value of callback.
          resolve(callback.apply(null, arguments));
        }
      });

    })
  );
});


parser.on('close', function() {
  Promise.all(promises).then(function(values) {
    dismiss(); // All resolved.
  }, function(reason) {
    console.error(reason.stack); // First reject.
    process.exit(); // Exit on error.
  });
});
Run Code Online (Sandbox Code Playgroud)

要学习 Promise,您应该阅读 Mozilla 文档或其他一些有信誉的参考资料或教程,但我在下面添加了一些应该对您有所帮助的要点。


新的承诺(fn)

当你使用构造函数创建一个新的 Promise 时,你传入的函数会立即被调用。这对于立即启动异步任务然后能够使用接口尽早响应其结果是必要的.then()。在您传入的函数中,您可以resolve选择任意的rejectPromise。


使用 Promise.all

Promise.all(iterable) 方法返回一个承诺,当可迭代参数中的所有承诺都已解决时,该承诺将得到解决,或者由于第一个传递的承诺被拒绝而被拒绝。

需要注意的重要事项是:

Promise.all([
  new Promise((resolve, reject) => setTimeout(() => resolve('work done'), 3000)),
  new Promise((resolve, reject) => reject('coffee depleted'))
]).then(
  (v) => console.log('all work done!'),
  (r) => console.error(`Error: ${r}`)
)
Run Code Online (Sandbox Code Playgroud)

立即拒绝,而不等待第一个 Promise 解决,因为第二个 Promise 给出了早期错误数组中的第一个 Promise 仍将最终解决,但重点是Promise.all尽早拒绝,这很好。


垫片

如果您运行的是没有 Promises 的旧版本 Node,则可以安装Promises 库,这是 GitHub 上提供的开源实现。

npm install promise --save
Run Code Online (Sandbox Code Playgroud)

那么就require这样:

var Promise = require('promise');
Run Code Online (Sandbox Code Playgroud)

es6-promiseify

您可以承诺异步函数,handleFields假设它是一个异步 Node I/O 风格的操作,调用回调err作为其第一个参数:

// npm install es6-promisify --save
var promisify = require("es6-promisify")

handleField = promisify(handleField);
promises.push(
  handleField().then(function(successArgs) {
    // success callback here
  }).catch(function(err) {
    console.error(err);
  })
);
Run Code Online (Sandbox Code Playgroud)

总的来说,这看起来很干净。如果您使用 Promise 库,那么只需使用Promise.denodify(fn).