在 node.js 中返回一组 N 个异步调用的第一个/最快的 M 结果的最有效方法是什么



我正在将一个慢速异步谓词函数(它调用外部HTTP API)应用于N个输入的列表。我只需要函数返回 true 的任何 M(其中 M <= N)输入。我的第一个尝试是:

var async = require('async');
function asyncFilterFirstM(inputs, m, fn, finalCb) {
  var outs = [];
  var alreadyReturned = false;
  async.map(inputs, function(input, cb) {
    fn(input, function(ret) {
      if (ret) {
        outs.push(input);
        if (outs.length === m) {
          alreadyReturned = true;
          finalCb(null, outs);
        }
      }
      cb(null, ret);      
    });
  }, function(err) {
    if (!alreadyReturned) {
      if (err) return finalCb(err);
      finalCb(null, outs);
    }
  });
}

此版本的问题是:

  1. 其余 M - N 个调用不会取消。
  2. finalCb不一定立即调用。它往往会阻止其余正在进行的慢速调用,从而破坏预期优化的目的。

如果您希望您的函数在所有操作完成之前返回,那么我认为您不能使用 async#map。此外,异步不提供任何用于取消正在运行的任务的 API。

这是我的做法。每个迭代器都可以返回一个中止函数。它可以像这样简单:

function (input, done) {
  var req = http.createClient();
  …
  return function () {
    req.abort();
  }
}

如果您正在执行 HTTP 请求。

function asyncMapFastest(arr, m, iterator, done) {
  var remaining = m,
      results = {},
      aborts = [],
      finished = false;
  function abortStillRunning() {
    finished = true;
    aborts.forEach(function (abort) {
      if (abort)
        abort();
    });
    aborts = [];
  }
  arr.forEach(function (input, i) {
    var abort = iterator(input, function (err, result) {
      if (finished)
        return;
      if (err) {
        abortStillRunning();
        return done(err);
      }
      remaining--;
      results[input] = result;
      aborts[i] = null;
      if (remaining === 0) {
        abortStillRunning();
        return done(null, results);
      }
    });
    aborts[i] = abort;
  });
}

相关内容

最新更新