繁体   English   中英

如何在拒绝上链接承诺

[英]How to chain promises on reject

给定一个函数, fn ,它返回一个promise,以及一个任意长度的数据数组(例如data = ['apple', 'orange', 'banana', ...] )你如何在每个元素上链接函数调用序列中的数组,如果fn(data[i])解析,整个链完成并停止调用fn ,但如果fn(data[i])拒绝,则执行下一个调用fn(data[i + 1])

这是一个代码示例:

// this could be any function which takes input and returns a promise
// one example might be fetch()
const fn = datum =>
  new Promise((resolve, reject) => {
    console.log(`trying ${datum}`);

    if (Math.random() < 0.25) {
      resolve(datum);
    } else {
      reject();
    }
  });

const foundResult = result => {
  // result here should be the first value that resolved from fn(), and it
  // should only be called until the first resolve()
  console.log(`result = ${result}`);
};

// this data can be purely arbitrary length
const data = ['apple', 'orange', 'banana', 'pineapple', 'pear', 'plum'];

// this is the behavior I'd like to model, only for dynamic data
fn('apple').then(foundResult)
  .catch(() => {
    fn('orange').then(foundResult)
      .catch(() => {
        fn('banana').then(foundResult)
          .catch(() => {
            /* ... and so on, and so on ... */
          });
      });
  });

我觉得也许这种模式的优雅解决方案是我所缺少的。 这个行为与Array.some()非常相似,但是我试图摆弄它是空洞的。

编辑:我从数字数据切换到字符串,以强调解决方案不需要依赖于数字数据。

编辑#2:为了进一步澄清, fn可以是任何接受输入并返回promise的函数。 上面的fn实现只是为了给出一个完整的例子。 实际上, fn实际上可能类似于API请求,数据库查询等。

您可以使用async/await和循环:

async function search() {
  for (let item of data) {
    try {
      return await fn(item);
    } catch (err) { }
  }
  throw Error ("not found"); 
}

search().then(foundResult).catch(console.log);
  • fn可以返回Promise(等待)或只返回一个值(返回)
  • 您的data可能是无限iterable序列(生成器)
  • 在我看来,它也易于阅读和理解意图。

如果序列失败,这是输出:

trying apple
trying orange
trying banana
trying pineapple
trying pear
trying plum
Error: not found

对于异步的支持是在es2017天然的,但可transpiled到ES3 / ES5与巴别打字稿

您可以使用Array.reduce来获取所需的数据。

data.reduce((promise, item) => promise.then(
  (param) => {
    if (param) return Promise.resolve(param);
    return fn(item).catch(() => Promise.resolve());
  } 
), Promise.resolve())
.then(foundResult)

基本上它会在结果通过后将结果传递给结束。 如果fn失败,它将通过未定义值的promise传递给下一个链以触发fn

编写如下搜索功能:

function search(number) {
    if (number < data.length) {
        fn(data[number]).then(foundResult)
              .catch(() => search(number + 1));
    }
}

search(0);

你可以编写一个非常简单的递归函数,它将在第一个解析时停止并在catch上递归。

function find_it(arr) {
   let [head, ...rest] = arr

   if (!head) return console.log("not found") // all rejects or no data

   fn(head)
   .then(r => foundResult(r) )
   .catch(r => find_it(rest))
}
find_it(data)

如果找到匹配并且不关心data长度,除非超过递归中的堆栈大小,否则这有利于在第一次匹配时停止而不调用所有值。 当然,当所有承诺都拒绝做某事时,修改边缘情况的动作会很容易。

找到结果:

$ node ./test
尝试苹果
尝试橙色
结果=橙色

并没有找到:

$ node ./test尝试苹果
尝试橙色
尝试香蕉
尝试菠萝
尝试梨
尝试梅花
未找到

你想要做的就是这样做我猜(它不是严格等同的,见下面的储备):

const fn = n => new Promise(() => {});
const seq = [];
const process = n => fn(n).then(foundResult);

seq.slice(1).reduce((operation, item) => {

    return operation
        .catch(() => process(item));

}, process(seq[0]));

使用Promise.race

const fetchAll = ( urls = [] ) => Promise.race(urls.map(fn)).then(foundResult);

但是我不确定这是你想要实现的:例如在你的代码片段中你没有在catch处理程序中返回任何内容,这意味着foundResult很可能是副作用。 此外,在阅读代码片段时,您正在捕获可以从foundResult内部而不是fn函数中引发的错误。

作为一个经验法则,我总是试图通过已知的“类型”履行我的承诺,或者拒绝错误。 在您的示例中,不清楚您生成的承诺将以任何值结算,无论是rejectionValue还是fulfillmentValue

也许如果你提供一个用例,我可以帮助你一点。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM