首页 文章

如何在拒绝上链接承诺

提问于
浏览
6

给定一个函数, fn ,它返回一个promise,以及一个任意长度的数据数组(例如 data = ['apple', 'orange', 'banana', ...] ),如何依次对数组的每个元素进行函数调用,这样如果_1215603解析,整个链完成并停止调用 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 ... */
          });
      });
  });

我觉得可能还有's an elegant solution to this pattern that I'失踪了 . 这种行为与 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. The fn implementation above was just to give a complete example. In reality, fn could actually be something like an API request, a database query, etc.

5 回答

  • 5

    你可以使用 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中是原生的,但可以用babeltypescript转换为es3 / es5

  • 4

    您可以使用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 失败,它会将未定义的值承诺传递给下一个链以触发 fn .

  • 1

    编写如下搜索功能:

    function search(number) {
        if (number < data.length) {
            fn(data[number]).then(foundResult)
                  .catch(() => search(number + 1));
        }
    }
    
    search(0);
    
  • 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尝试苹果尝试橙色尝试香蕉尝试菠萝尝试梨尝试梅没有找到

  • 2

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

    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 函数中引发的错误 .

    作为一个经验法则,我总是试图通过已知的"type"履行我的承诺,或者拒绝错误 . 它's not clear in your example that the promise you are generating will be settled with any value, whether it'是 rejectionValuefulfillmentValue .

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

相关问题