按顺序执行Promise映射

8

我写了一个在循环(map)中被调用且使用promise的函数。现在,我希望该函数同步运行并在下一次被调用之前退出。

function t1(){
  let arr1 = [1,2,3,4,5];
  return Promise.map(arr1, (val) =>{
    const params = {
      "param1" : val1
    };
    return t2(params);
  });
}

function t2(event){
  return Promise.resolve()
  .then({
  //do something
  //code doesn't reach here in sync manner. all five instance are invoked and then code reaches here for first instance and so on
  })
  .then({
  //promise chaining. do something more
  })
}

t2被调用了五次,但我希望每个实例在前一个实例返回值之后才被调用。目前它并不会按照这样的方式行为,而是同时调用这五个函数。

由于项目限制,我不能使用async/await。


Promise.map是什么?你使用的是bluebird还是其他的promise库? - Bergi
3个回答

13

您现在的代码存在问题,Promise.prototype.mapforEach一样,不会等待在其内部调用的异步函数完成。(除非您使用await.then显式地告诉解释器要等待异步调用)

请让t1等待每个t2的调用:


async function t1(){
  let arr1 = [1,2,3,4,5];
  const results = [];
  for (const val of arr1) {
    results.push(await t2(val));
  }
  return results;
}

或者如果你想使用reduce而不是async/await

const delay = () => new Promise(res => setTimeout(res, 500));
function t1(){
  let arr1 = [1,2,3,4,5];
  return arr1.reduce((lastProm, val) => lastProm.then(
    (resultArrSoFar) => t2(val)
      .then(result => [...resultArrSoFar, result])
  ), Promise.resolve([]));
}

function t2(event){
  return delay().then(() => {
    console.log('iter');
    return event;
  });
}

t1()
  .then(results => console.log('end t1', results));

或者,如果您需要将顺序功能封装在 t2 中,则让 t2 具有先前生成的 Promise 的半持久性变量:

const delay = () => new Promise(res => setTimeout(res, 500));
const t1 = () => {
  return Promise.all([1, 2, 3, 4].map(t2));
};

const t2 = (() => {
  let lastProm = Promise.resolve();
  return (event) => {
    const nextProm = lastProm
      .catch(() => null) // you may or may not want to catch here
      .then(() => {
        // do something with event
        console.log('processing event');
        return delay().then(() => event);
      });
    lastProm = nextProm;
    return nextProm;
  };
})();

t1().then(results => console.log('t1 done', results));


1
不建议修改t2以保持队列本身,而是建议在t1中使用传统的reduce方法。 - Bergi
您能否帮助我理解我的方法哪里出错了? - user3807691
1
@user3807691 如果没有使用 "await",所有的 JavaScript 代码都会在不等待任何东西的情况下执行。因此,顺序代码永远不会等待 Promise 解决。如果您无法使用 await,则必须编写逻辑,仅启动单个基于 Promise 的函数并在回调(then)中调用下一个函数。 - Falco
@Falso 谢谢!帮助我更好地理解了。虽然我原本认为如果我们使用'return'语句,它会使promise等待并因此同步运行。 - user3807691
1
只有 await.then 可以让解释器在继续之前等待 Promise 完成。 - CertainPerformance

1
(function loop(index) {
    const next = promiseArray[index];
    if (!next) {
        return;
    }
    next.then((response) => {
        // do Something before next
        loop(index + 1);
    }).catch(e => {
        console.error(e);
        loop(index + 1);
    });
})(0 /* startIndex */)

1
我会将“index+1”作为参数传递给下一次循环(index)的调用,这样我们就不需要管理全局状态变量(感觉不对,并且如果在代码的其他部分更改“index”,可能会变得混乱)。 - Falco
我会放弃.catch()。如果有错误,我们不希望循环继续。特别是如果错误来自递归的loop(index + 1)调用,我们更不希望它继续进行。 - Bergi
等等,promiseArray是什么?如果你不在loop内部构建单个的promise,而是在之前创建它们并将它们放入数组中,它们仍然不会按顺序运行。 - Bergi

-1

这是使用.reduce()与async/await结合顺序运行Promises的样子:

async function main() {

  const t2 = (v) => Promise.resolve(v*2)
  const arr1 = [1,2,3,4,5];
  
  const arr1_mapped = await arr1.reduce(async (allAsync, val) => {
    const all = await allAsync
    all.push(await t2(val) /* <-- your async transformation */) 
    return all
  }, [])

  console.log(arr1_mapped)

}

main()


抱歉,但为什么会有负评?它按预期工作,我自己也在使用。 :D - phil294
1
a) 使用reduceawait是可怕的代码 b) OP明确表示他们不能使用async/await - Bergi
2
好的。我认为这是一个比接受答案中reduce示例更漂亮的替代方案。使用Coffeescript,它甚至看起来非常不错。但严格来说,它并没有回答问题,你是对的。 :-) - phil294

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接