如何串行执行多个promise

只使用promise语法。不使用async、await等语法。

PS:网上广为流传的使用reduce实现多个promise串行执行只对同步promise生效。

function start(tasks){
  var result = []
  return tasks.reduce((accumulator,item,index)=>{
    return item.then(res=>{
      result[index] = res
      return index == tasks.length - 1 ? result : item
    })
  },Promise.resolve())
}
start([
  Promise.resolve(1),
  Promise.resolve(2),
  Promise.resolve(3)
]).then(res=>{
  console.log(res) //[1,2,3]
})

串行执行成功

function start(tasks){
  var result = []
  return tasks.reduce((accumulator,item,index)=>{
    return item.then(res=>{
      result[index] = res
      return index == tasks.length - 1 ? result : item
    })
  },Promise.resolve())
}
function delay(time){
  return new Promise(function(resolve,reject){
    setTimeout(function(){
      resolve(time)
    },time)
  })
}

start([
  delay(2000),
  delay(2000),
  delay(1000)
]).then(res=>{
  console.log(res) //[undefined,undefined,1000]
})

串行执行不成功

阅读 9.3k
6 个回答

reduce实现多个promise串行执行的关键在于不断的在accumulator后面追加then,你这里缺少了,应该改为

function start(tasks){
      var result = []
      return tasks.reduce((accumulator,item,index)=>{
        return accumulator.then(res=>{
            return item.then(res=>{
                  result[index] = res
                  return index == tasks.length - 1 ? result : item
            })
        })
      },Promise.resolve())
}

你的问题不在于没有串行执行,而是在声明

[
  delay(2000),
  delay(2000),
  delay(1000)
]

的时候代码已经开始执行了,所有的计时器已经被激活了了。所以无论start怎么做,你都没法得到等5秒完成所有任务的结果。

概念问题
创建一个promise时已经“开始执行”了
promise创建后,能做的只是 “等待执行完成”

其实这个问题在于 因为你执行的delay是异步的... 在reduce的过程中他们每个方法都被赋予了一个浅层次index...
然后同一时间都在等then的触发... 谁快执行谁... 这时候最后一个1000ms的先完事了...正好它的 index == tasks.length - 1 然后就把result返回了 这时候就是[undefined,undefined,1000] 因为这时候前两个还没完事呢...

改变一下写法就好

function start(tasks) {
    var result = [];
    return tasks.reduce((accumulator, item, index) => {
    return accumulator.then(res => {
      result.push(res);
      return index == tasks.length - 1 ? item.then(res => { result.push(res); return result; }) : item;
    });
    }, Promise.resolve(0));
}

function delay(time) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(time);
        }, time);
    });
}

start([delay(3000), delay(2000), delay(1000)]).then(res => {
    console.log(res); // [0, 3000, 2000, 1000]
});

我没理解错的话,你只需要保证结果有序,并不需要保证执行有序,所以一个promise.all就搞定了。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题