如何在.map中使用多个等待?

时间:2019-09-10 15:44:23

标签: javascript asynchronous async-await

我知道我们不能简单地做类似的事情:

myArray.forEach(async x => await asyncOperation())

编辑:我知道这是有效的,但是我需要保持正确的顺序

如果必须使用异步操作遍历数组,则必须执行以下操作:

await Promise.all(myArray.map(x => asyncOperation()))

但是,我需要在同一迭代中执行两个异步操作。我知道另一种替代方法是只使用.reduce,它看起来像这样:

await myArray.reduce((p, el) => {
      return p.then(() => {
        return somePromise(el)
          .then(res => {
            return anotherPromise(res)
          })
      })
    }, Promise.resolve())

但是我避免嵌套promise,并希望仅使用async/await保留它。所以无论如何,我的问题是,遍历需要经过两个promise的数组的最佳等待时间是什么?

5 个答案:

答案 0 :(得分:0)

您可以在单独的函数中执行彼此依赖的异步代码,该函数在地图中调用,然后将地图包装在Promise.all中。

const doAsyncStuff = async (x) => {
    const firstValue = await fakeLongTask1();
    const finalValue = await fakeLongTask2(firstValue);
    return finalValue;
}

Promise.all(myArray.map(x => {
    doAsyncStuff(x);
}));

这将解雇所有任务,但是等待彼此依赖的长任务,然后一旦完成,Promise.all就解决了。

答案 1 :(得分:0)

您可以将async循环与递归函数一起使用,即使我不能使用async / await,我通常也采用这种方式编写代码

a

这应该是一种模式,我一直在这样做,但是通常看起来像这样(我仍然手动编写一些ES5代码)。

return (async function next() {
    var item = arr.shift();
    if (shift) {
      var res = await doSomething(item);
      await doSmethingElse(res);
      next();
    }
})();

如果要累积doSmethingElse的结果,只需将其添加到数组中,然后传递以与async / await示例一样进行解析。

return new Promise(resolve) {
   (function next() {
       var item = arr.shift();
       if (item) {
          doSomething(item).then(doSmethingElse).then(next);
       } else {
          resolve();
       }
   })();
});

答案 2 :(得分:-1)

无耻插入插件,我建议使用npm package bs-better-stream,它专门研究异步流/数组。

例如,它可能很简单:

const stream = require('bs-better-stream');

let s = stream()
  .write(...myArray)
  .map(el => somePromsie(el))
  .wait() // or waitOrdered() to preserve original order
  .map(res => anotherPromise(res))
  .wait();

如果要转换回原始数组,它会稍微冗长一些,因为您需要等待两组承诺都可以解决。但是它仍然比.reduce(await Promise.all(...)).map更具可读性:

const stream = require('bs-better-stream');

let somePromise = a => Promise.resolve(a * 2);
let anotherPromise = a => Promise.resolve(-a);

let x = async myArray => {
    let promises = stream()
        .write(...myArray)
        .map(el => somePromise(el));

    let promises2 = promises
        .wait()
        .map(res => anotherPromise(res));

    await promises.promise;
    return promises2.promise; // -2, -4, -6, -8
}

x([1, 2, 3, 4]).then(a => console.log(a));

此方法的另一个优点是,它允许所有异步功能并行发生。即第二组承诺不必等待所有第一组承诺完成。

答案 3 :(得分:-1)

async / await很棒,但是它们并不是所有使用承诺的代码的100%替代。有时(例如,当您需要使用Promise.all时),您会需要将承诺本身用作值,而不是在async / await内部秘密使用。

种方式可以避免这种情况,例如避开ES6 map和其他数组迭代方法,而是使用for循环...但通常这样做是...治愈比疾病还糟。只是使用诺言。

编辑:

这是一个基于评论反馈的基本示例。假设您有两个(或多个)URL,并且想获取第一个(然后返回),然后获取下一个,一旦全部获取,则执行其他操作。如果您尝试与ES6数组方法同步执行此操作,即使您使用async / await,也将无法使用:

const urls = ['www.example.com/1', 'www.example.com/2'].
var promises = urls.map(async url => await fetch(url));
Promise.all(promises).then(doSomethingElse);

该代码将实际执行的操作是立即获取所有URL。 不会等待获取第二个,直到第一个完成。

为此,您可以 可以将async / awaitfor循环一起使用,并避免使用ES6(大概在{{1 }}函数):

async

...但是您正在使用const urls = ['www.example.com/1', 'www.example.com/2']. const pageHtml = []; for (var i = 0; i < promises.length; i++) { const url = urls[i]; const html = await fetch(url)); pageHtml.push(html); } doSomethingElse(); // no need for Promise.all! 循环,就像1999年一样; ick!我认为更好的解决方案是使用现代Javascript,但不要对所有 使用for / async

最多使用 个关键字,但是随后当您需要使用await时,可以使用递归函数(如果需要“ fetch#1 AFTER#2”部分;参见@jcubic的答案以获取可能的实现),或者如果您不只是这样做:

Promise.all

是的 是避免使用const urls = ['www.example.com/1', 'www.example.com/2']. var promises = urls.map(fetch); Promise.all(promises).then(doSomethingElse); / async并使用数组或Promise对象  直接使用await ...,但是代码简短明了,可以满足您的要求。您不必总是使用这些关键字。

答案 4 :(得分:-1)

这是我有时使用的功能,可以按顺序遍历长任务。

var arr = ['do', 're', 'mi', 'fa', 'so', 'la', 'ti'];

const f1 = () => new Promise(d => setTimeout(d, 250));
const f2 = () => new Promise(d => setTimeout(d, 250));

(async() => {
  await asyncLoop(arr, async(itm, idx, fin) => {

    console.log('doing something async with ', itm);
    await f1();

    console.log('doing something async with ', itm, ' again');
    await f2();

    fin(); // start the next loop

  });
  
  console.log('done looping');
})();



function asyncLoop(items, loopBody) {
  return new Promise(f => {
    let done = arguments[2] || f;
    let idx = arguments[3] || 0;
    let cb = items[idx + 1] ? () => asyncLoop(items, loopBody, done, idx + 1) : done;
    loopBody(items[idx], idx, cb);
  });
}

为方便起见,它位于https://www.twilio.com/docs/voice/api/call#update-a-call-resource上。