Bluebird Promise.any() 提前拒绝

Bluebird Promise.any() early reject?

本文关键字:拒绝 Promise any Bluebird      更新时间:2023-09-26

我正在我所有的Node.js项目中使用promise库Bluebird。为了从文件路径列表中获取第一个现有文件的内容,我成功地使用了Promise.any如下:

Promise.any([
   './foo/file1.yaml',
   './foo/file2.yaml',
   './foo/file3.yaml'
], function(filePath) {
    _readFile(filePath);
}),then(function(fileContent) {
    _console.log(fileContent);
});

我的问题是,如果我在读取文件时收到与"找不到文件"不同的错误,如何提前离开Promis.any循环?下面的代码说明了我的问题:

Promise.any([
   './foo/file1.yaml',
   './foo/file2.yaml',
   './foo/file3.yaml'
], function(filePath) {
    _readFile(filePath)
    .catch(function(err) {
       var res = err;
       if (err.code == FILE_NOT_FOUND) {
          // continue the Promise.any loop
       } else {
          // leave the Promise.any loop with this error
          err = new Promise.EarlyBreak(err);
       }
       Promise.reject(err);
    });
}).then(function(fileContent) {
    _console.log(fileContent);
}, function(err) {
    // the first error different from FILE_NOT_FOUND
});

可能Promise.any不是正确的功能?

提前离开Promise.any()循环在概念上是有问题的,因为Promise.any()是一个聚合器而不是一个循环,并且接受一系列承诺,每个承诺都有自己的生命,而不是由Promise.any()决定的。

但是,从路径数组开始,您寻找的循环可以表示为paths.reduce(...)表达式,该表达式构建一个.catch()链,简单如下:

function getFirstGoodFileContent(paths) {
    paths.reduce(function(promise, path) {
        return promise.catch(function() {
            return _readFile(path);
        });
    }, Promise.reject()); // seed the chain with a rejected promise.
}

捕捞链:信用贝尔吉

这样构建的 .catch 链将在失败时进入下一次迭代,或者在成功时跳到链的末尾。这种流量控制与更正常的 .then 链(播种了兑现的承诺(中发生的情况相反。

但这还不是全部。需要一个额外的条件 - 即"如果我收到与'找不到文件'不同的错误,请尽早离开 [Promise.any] 循环"。通过发送除成功路径FILE_NOT_FOUND以外的所有错误,这非常简单地设计到捕获链中,从而:

  • 影响所需的流量控制(跳过链的其余部分(,但
  • 最终以错误条件沿着成功路线前进 - 不希望但可以恢复。
function getFirstGoodFileContent(paths) {
    paths.reduce(function(promise, path) {
        return promise.catch(function() {
            return _readFile(path).catch(function(err) {
                if (err.code == FILE_NOT_FOUND) {
                    throw err; // Rethrow the error to continue down the catch chain, seeking a good path.
                } else {
                    return { isError: true, message: err.code }; // Skip the rest of the catch chain by returning a "surrogate success object".
                }
            });
        });
    }, Promise.reject()).then(function(fileContent) {
        // You will arrive here either because :
        // * a good path was found, or
        // * a non-FILE_NOT_FOUND error was encountered.
        // The error condition is detectable by testing `fileContent.isError`
        if (fileContent.isError) {
            throw new Error(fileContent.message); // convert surrogate success to failure.
        } else {
            return fileContent; // Yay, genuine success.
        }
    });
}

所以你现在可以打电话:

getFirstGoodFileContent([
    './foo/file1.yaml',
    './foo/file2.yaml',
    './foo/file3.yaml'
]).then(function(fileContent) {
    _console.log(fileContent);
}, function(error) {
    // error will be due to :
    // * a non-FILE_NOT_FOUND error having occurred, or
    // * the final path having resulted in an error.
    console.log(error); 
});