【问题标题】:Node promises: use first/any result (Q library)节点承诺:使用第一个/任何结果(Q 库)
【发布时间】:2017-04-29 00:12:31
【问题描述】:

我知道使用 Q 库很容易等待多个承诺完成,然后使用与这些承诺结果对应的值列表:

Q.all([
    promise1,
    promise2,
    .
    .
    .
    promiseN,
]).then(results) {
    // results is a list of all the values from 1 to n
});

但是,如果我只对单一的、最快完成的结果感兴趣,会发生什么?举一个用例:假设我有兴趣检查大量文件,并且只要找到包含“津巴布韦”一词的任何文件,我就会满足。

我可以这样做:

Q.all(fileNames.map(function(fileName) {
    return readFilePromise(fileName).then(function(fileContents) {
        return fileContents.contains('zimbabwe') ? fileContents : null;
    }));
})).then(function(results) {
    var zimbabweFile = results.filter(function(r) { return r !== null; })[0];
});

但即使我已经找到“津巴布韦”,我也需要完成每个文件的处理。如果我有一个包含“zimbabwe”的 2kb 文件和一个不包含“zimbabwe”的 30tb 文件(假设我正在异步读取文件) - 那太愚蠢了!

我想要做的是在任何承诺得到满足的那一刻得到一个值:

Q.any(fileNames.map(function(fileName) {
    return readFilePromise(fileName).then(function(fileContents) {
        if (fileContents.contains('zimbabwe')) return fileContents;
        /*
        Indicate failure
        -Return "null" or "undefined"?
        -Throw error?
        */
    }));
})).then(function(result) {
    // Only one result!
    var zimbabweFile = result;
}).fail(function() { /* no "zimbabwe" found */ });

使用这种方法,如果早期在我的 2kb 文件中发现“津巴布韦”,我将不会等待我的 30tb 文件。

但是没有Q.any这样的东西!

我的问题:如何获得这种行为?

重要提示:即使在其中一个内部承诺中发生错误,它也应该返回没有错误。

注意:我知道我可以通过在找到第一个有效值时抛出错误来破解 Q.all,但我更愿意避免这种情况。

注意:我知道类似Q.any 的行为可能是不正确的,或者在许多情况下是不恰当的。请相信我有一个有效的用例!

【问题讨论】:

  • 不知道 Q 是否支持它,但原生承诺肯定支持。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • @KevinB 好点,已修复!我想混合 Q 和原生承诺不被认为是好的?你知道这方面的共识是什么吗?
  • @KevinB 在其他新闻中,实际上有 Q.race 这样的东西 - 它没有记录,但它存在。如果您添加答案,我会给您支票!
  • race 方法在第一次拒绝时拒绝...
  • @Hitmands 你说的是原生的race 方法吗?我将对Q 中的那个做一些快速测试,看看它是否符合我的用例。

标签: node.js promise q cancellation


【解决方案1】:

您将两个不同的问题混为一谈:竞速和取消。

比赛很容易,可以使用Promise.race,或者你最喜欢的promise库中的等价物。如果您愿意,您可以自己编写大约两行代码:

function race(promises) {
  return new Promise((resolve, reject) => 
    promises.forEach(promise => promise.then(resolve, reject)));
}

如果任何 Promise 被拒绝,那将被拒绝。相反,如果您想跳过拒绝,并且仅在所有承诺都拒绝时才拒绝,那么

function race(promises) {
  let rejected = 0;
  return new Promise((resolve, reject) => 
    promises.forEach(promise => promise.then(resolve,
      () => { if (++rejected === promises.length) reject(); }
    );
}

或者,您可以将promise inversion trickPromise.all 一起使用,我不会在这里介绍。

你真正的问题是不同的——你显然想在其他承诺解决时“取消”其他承诺。为此,您将需要额外的专业机器。代表每个处理段的对象将需要某种方式来要求它终止。这是一些伪代码:

class Processor {
  promise() { ... }
  terminate() { ... }
}

现在你可以将你的种族版本写成

function race(processors) {
  let rejected = 0;
  return new Promise((resolve, reject) => 
    processors.forEach(processor => processor.promise().then(
      () => {
        resolve();
        processors.forEach(processor => processor.terminate());
      },  
      () => { if (++rejected === processors.length) reject(); }
    );
  );
}

有各种提议来处理承诺取消,这些提议在几年后实施时可能会变得更容易。

【讨论】:

    猜你喜欢
    • 2017-05-08
    • 2014-03-23
    • 1970-01-01
    • 2021-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-21
    • 1970-01-01
    相关资源
    最近更新 更多