简体   繁体   English

如何在拒绝上链接承诺

[英]How to chain promises on reject

Given a function, fn , which returns a promise, and an arbitrary length array of data (eg data = ['apple', 'orange', 'banana', ...] ) how do you chain function calls on each element of the array in sequence, such that if fn(data[i]) resolves, the whole chain completes and stops calling fn , but if fn(data[i]) rejects, the next call fn(data[i + 1]) executes? 给定一个函数, fn ,它返回一个promise,以及一个任意长度的数据数组(例如data = ['apple', 'orange', 'banana', ...] )你如何在每个元素上链接函数调用序列中的数组,如果fn(data[i])解析,整个链完成并停止调用fn ,但如果fn(data[i])拒绝,则执行下一个调用fn(data[i + 1])

Here is a code example: 这是一个代码示例:

// 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 ... */
          });
      });
  });

I feel like maybe there's an elegant solution to this pattern that I'm missing. 我觉得也许这种模式的优雅解决方案是我所缺少的。 The behavior is very similar to Array.some() , but I've come up empty trying to fiddle with that. 这个行为与Array.some()非常相似,但是我试图摆弄它是空洞的。

EDIT: I switched from numeric data to string to stress that the solution needs to not be reliant on the data being numeric. 编辑:我从数字数据切换到字符串,以强调解决方案不需要依赖于数字数据。

EDIT #2: Just to clarify further, fn could be any function that accepts input and returns a promise. 编辑#2:为了进一步澄清, fn可以是任何接受输入并返回promise的函数。 The fn implementation above was just to give a complete example. 上面的fn实现只是为了给出一个完整的例子。 In reality, fn could actually be something like an API request, a database query, etc. 实际上, fn实际上可能类似于API请求,数据库查询等。

You could use async/await and a loop: 您可以使用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 can return either Promise (awaited) or simply a value (returned) fn可以返回Promise(等待)或只返回一个值(返回)
  • your data could be an infinite iterable sequence (generator) 您的data可能是无限iterable序列(生成器)
  • in my opinion, its also easy to read and understand intent. 在我看来,它也易于阅读和理解意图。

here is the output if the sequence fails: 如果序列失败,这是输出:

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

support for async is native in es2017, but can be transpiled to es3/es5 with babel or typescript 对于异步的支持是在es2017天然的,但可transpiled到ES3 / ES5与巴别打字稿

You can use Array.reduce to get the desired data. 您可以使用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)

Basically it will pass over the result to the end once passes. 基本上它会在结果通过后将结果传递给结束。 And if fn is failed, it will pass over undefined valued promise to next chain to trigger fn . 如果fn失败,它将通过未定义值的promise传递给下一个链以触发fn

Write a search function like below: 编写如下搜索功能:

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

search(0);

You could write a very simple recursive function that will stop on the first resolve and recurse on the catch. 你可以编写一个非常简单的递归函数,它将在第一个解析时停止并在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)

This has the benefit of stopping on the first match without calling all the values if a match is found and not caring about the length of data unless you exceed the stack size in the recursion. 如果找到匹配并且不关心data长度,除非超过递归中的堆栈大小,否则这有利于在第一次匹配时停止而不调用所有值。 Of course it would be easy to modify the action on the edge case when all promises reject to do something. 当然,当所有承诺都拒绝做某事时,修改边缘情况的动作会很容易。

Result when found: 找到结果:

$ node ./test $ node ./test
trying apple 尝试苹果
trying orange 尝试橙色
result = orange 结果=橙色

and not found: 并没有找到:

$ node ./test trying apple $ node ./test尝试苹果
trying orange 尝试橙色
trying banana 尝试香蕉
trying pineapple 尝试菠萝
trying pear 尝试梨
trying plum 尝试梅花
not found 未找到

What you are trying to do could be done like this I guess (it is not strictly equivalent, see reserves below): 你想要做的就是这样做我猜(它不是严格等同的,见下面的储备):

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]));

With Promise.race : 使用Promise.race

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

However I am not sure this is what you are trying to achieve : for instance in your snippet you are not returning anything in your catch handler which means that the foundResult is most likely a side effect. 但是我不确定这是你想要实现的:例如在你的代码片段中你没有在catch处理程序中返回任何内容,这意味着foundResult很可能是副作用。 Moreover when reading your code snippet you are catching errors that can be raised from inside the foundResult and not from the fn function. 此外,在阅读代码片段时,您正在捕获可以从foundResult内部而不是fn函数中引发的错误。

As a rule of thumb I try to always have my promises fulfilled by a known "type" or rejected with an error. 作为一个经验法则,我总是试图通过已知的“类型”履行我的承诺,或者拒绝错误。 It's not clear in your example that the promise you are generating will be settled with any value, whether it's a rejectionValue or a fulfillmentValue . 在您的示例中,不清楚您生成的承诺将以任何值结算,无论是rejectionValue还是fulfillmentValue

Perhaps if you would provide a use case I could help you a bit. 也许如果你提供一个用例,我可以帮助你一点。

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

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