【问题标题】:Invoking an array functions that return promises in order without external dependencies调用数组函数,按顺序返回 Promise,无需外部依赖
【发布时间】:2017-04-03 00:53:12
【问题描述】:

不使用任何额外的库(async、bluebird 等) 我正在尝试实现一个函数,该函数返回一个基于返回 Promise 的函数数组的 Promise 解析(或拒绝)输入参数...Promise.all(iterable) 与我尝试完成的功能非常相似,但 Promise.alliterable 参数中的承诺不按顺序执行。

我可以简单地将这些函数链接在一起,但是如果functionList是一个未知长度的列表...

我已经尝试在概念上展示我想要在下面完成的工作:

function foo() {
  return new Promise((resolve, reject) => {
    setTimeout( () => {
        return resolve();
       }, 200);
  })
}

function bar() {
  return new Promise((resolve, reject) => {
    setTimeout( () => {
        return resolve();
       }, 200);
  })
}

function baz() {
  return new Promise((resolve, reject) => {
    setTimeout( () => {
        return reject();
       }, 100);
  })
}

const functionList = [foo, bar, baz];

function promiseSeries(functionList){
    const results = [];
    return new Promise((resolve, reject) => {
      promiseList.map((promise, i) => {
        return new Promise((_resolve, _reject) => {
          promise()
            .then((result) => {
              results.push(result);
              if (i === promiseList.length - 1) {
                return resolve(results);
              }
               else return _resolve();
            })
            .catch(error => error)
        })
      })
    })
  }

显然在 functionList 上运行 Promise.all 我们会看到 baz(即使它的位置是 functionList[2] 首先解析

Resolve promises one after another (i.e. in sequence)?Running promises in small concurrent batches (no more than X at a time) 均未提供此问题的答案。我不想导入一个库来处理这个单一的实用函数,而且我只是好奇这个函数会是什么样子。

【问题讨论】:

  • 没有任何其他库,你需要为此做一个递归,因为你需要等待一个承诺完成,一个例子(对不起,也可以作为评论发布): const recurse = ( promiseList) => { if (promiseList.length > 0) { promiseList[0]().then(v => recurse(promiseList.splice(1, promiseList.length)) } }
  • jsfiddle.net/jbnhLtvk/2 应该会给你这个想法。基本上,你需要等待一个promise完成,然后调用递归函数但没有数组的头部,所以最后数组将为空,函数将终止。
  • 这里更新了,jsfiddle.net/jbnhLtvk/4
  • 目前有一个关于堆栈溢出的类似问题,但它在外部依赖的上下文中与这个问题有关。那会是什么问题?

标签: javascript node.js promise


【解决方案1】:
promises.reduce((previous, promise) => {
  if (previous) {
    return previous.then(() => promise);
  }
  return promise;
}, null)

【讨论】:

    【解决方案2】:

    最简单的方法是链接它们,从一个 null 承诺开始:

    const promises = [foo, bar, baz];
    let result = Promise.resolve();
    promises.forEach(promise => result = result.then(() => promise));
    return result;
    

    正如 Ali 指出的,您可以使用归约来代替,但您需要在 then 调用中使用函数:

    return promises.reduce(
        (result, promise) => result.then(() => promise),
        Promise.resolve()
    );
    

    如果你知道 promises 不为空,你可以省略初始值。

    但是,如果您真的想按顺序执行操作,您通常需要处理一组返回 Promise 的函数。例如:

    return ids.map(id => () => processId(id))
    .reduce((p, fn) => p.then(fn), Promise.resolve());
    

    【讨论】:

      【解决方案3】:

      这并不比以下更复杂:

      funcs.reduce((prev, cur) => prev.then(cur), Promise.resolve())
      

      递归版本,返回解析值的数组:

      function recurse([first, ...last], init) {
        if (!first) return Promise.resolve([]);
        return first(init)
          .then(value => 
            recurse(last, value)
              .then(values => [value, ...values])
          );
      }
      
      // Testing function to return a function which
      // returns a promise which increments its value.
      const x = v => y => Promise.resolve(v + y);
          
      recurse([x(1), x(2)], 0).then(console.log);

      【讨论】:

      • 如果 A 和 B 是返回承诺的函数,我很困惑您的第一个示例与使用 Promise.all()... 没有显着不同...使用 Promise.all() 两个函数被同步调用(相反到 B 等待 A 解决)
      • const recurse = (promises, results) => { if (promises.length > 0) { return promises[0]() .then((res) => recurse(promises.splice(1, promises.length), results.concat(res))) .catch(err => Promise.reject(err)); } else { return Promise.resolve(results); } } 是最终满足我需求的功能...
      • 这两个答案让我困扰的是,在这两种情况下,如果任何函数返回的诺言都被拒绝,它将默默地失败——这两种解决方案都隐含地假设返回的每个诺言都是“thenable” ...
      • @Maxwell 你想做什么?无论哪种情况,最终结果都将是一个处于拒绝状态的承诺(原因是承诺被拒绝的原因),可以通过catch 处理。我想不出任何更合理的行为。所以例如recurse(blah).then(/*all promises fulfilled*/).catch(/*one promise was rejected*/).
      【解决方案4】:

      在没有任何额外库的情况下实现此目的的一种简洁明了的方法是使用递归。一个例子:

      const p1 = () => Promise.resolve(10);
      const p2 = () => Promise.resolve(20);
      const p3 = () => Promise.resolve(30);
      
      const promiseList = [p1, p2, p3];
      
      const sequencePromises = (promises) => {
        const sequence = (promises, results) => {
          if (promises.length > 0) {
            return promises[0]()
              .then(res => sequence(promises.splice(1, promises.length), results.concat(res)))
              .catch(err => Promise.reject(err));
          } else {
            return Promise.resolve(results);
          }
        }
        return sequence(promises, []);
      }
      
      sequencePromises(promiseList)
         .then(res => console.log(res))
         .catch(err => console.log("ERROR"));
      

      【讨论】:

      • @Maxwell .catch(err => Promise.reject(err)); 是无操作的(这是“什么都不做”的技术谈话)。
      • @torazaburo 那就去掉吧,我写代码的时候没注意。另一种方式,catch 是有用的,如果他想继续承诺,即使有一次失败。
      猜你喜欢
      • 1970-01-01
      • 2018-08-19
      • 2018-05-20
      • 2019-12-25
      • 1970-01-01
      • 1970-01-01
      • 2019-09-30
      • 2012-01-07
      • 2018-07-09
      相关资源
      最近更新 更多