javascript 如何并行运行异步函数?

acruukt9  于 2023-03-16  发布在  Java
关注(0)|答案(2)|浏览(155)

一个nodeJS脚本,里面有一个函数:

async myFunc1(a, b, c) {
  //.......
}

它返回一些值,但不是Promise。我曾经这样运行它:

let a1 = await myFunc1(1, "fds", 99);

现在我需要同时运行几十个函数,它们的数量和参数的输入来自用户。

let lines = readDataFromUserProvidedFile();
for (let i = 0; i < lines; i++) {
    //
    //how to run all the myFunc1(...) in parallel??
    //and then print out a result of each?

    //(?)
    myFunc1(lines[i], i, i*2);
}

这样的话我怎么并行运行呢?而且当后面的一个返回结果的时候,我想马上打印出来。
对于这个函数来说,返回一个值大约需要10分钟,它不进行计算,而是等待来自远程Web服务的结果。

jyztefdp

jyztefdp1#

更新日期:2023年3月16日

for...await从每次迭代的序列求值。它将按顺序遍历promises数组。如果数组中的第一个承诺花费10分钟,第二个承诺花费5分钟。它将不记录5分钟内完成的承诺。
“边走边记录”或在承诺完成时记录。有几种方法可以做到这一点。这将取决于您希望在代码的正常流程中有多少控制权。
您可以使用.forEach + .then

const lines = readDataFromUserProvidedFile();

const promises = lines.map((line, index) => myFunc1(line, index, index * 2));

promises.forEach(promise => {
  promise.then(result => {
    console.log(result);
  })
});

您可以删除.map并执行以下操作:

const lines = readDataFromUserProvidedFile();

lines.forEach((line, index) => {
  const promise = myFun(line, index, index * 2);
  promise.then(result => {
    console.log(result);
  });
})

通过这种方式,它将在每个问题得到解决时打印其结果--一个接一个
如果您希望更好地控制要解析的承诺,则需要手动管理每个承诺的状态。Promise.allPromise.allSettled将等到所有承诺完成后才将控制权移交给您/您的代码。这不是您想要的。
Promise对象represents an asynchronous操作的最终完成(或失败)及其结果值。您需要使用异步迭代对象来执行、控制和返回每个交互的值。
功能示例:

const lines = readDataFromUserProvidedFile();

const promises = lines.map((line, index) => myFun(line, index, index * 2));

for await (const result of execPromises(promises)) {
  console.log (result)
}

console.log("Completed!")
console.log(promises) // => it will be [] because of the mutable process

async function* execPromises(promises) {
  while (promises.length > 0) {
    const [index, value] = await Promise.race(promises.map((promise, index) =>
      promise.then((value) => [index, value])
    ));

    yield value;
    // 👇👇👇 mutable process!!!
    promises.splice(index, 1);
  }
}

由于Promise对象没有任何“状态”信息,我们需要手动使用.map来告诉代码在.then之后完成了哪个承诺。Promice.race将返回第一个已解析的承诺,在解析之后,我们使用.splice将其从数组中删除。
所有这一切都是因为没有状态信息“as-you-go”(例如,当它正在被解决时)。
JS生态系统中的库可以为您抽象这些内容,并使用队列创建更好的解决方案。
例如:

原始答案

你可以创建一个并行执行的承诺集合,然后在集合上迭代等待:
伪代码:

let lines = readDataFromUserProvidedFile();

const promises = lines.map((line, index) => myFunc1(line, index, index * 2));

// await the resolution of all promises and print the results as they become available
for await (const result of promises) {
  console.log(result);
}
j13ufse2

j13ufse22#

因此,承诺有两个部分。执行它们和等待它们。等待它们意味着当你得到一个结果时,你附加了一个回调来做某事。执行它们意味着只是“把它们踢出去”
问题是你使用的是await,启动异步进程和等待异步进程的结果是有区别的,具体来说,你可以启动一个异步进程,并在稍后将对它的引用保留给await,而不用立即对它进行await-ing。
tl;dr您仍然可以使用async/await,只需使用.map立即启动所有承诺:

const myFuncToBeCalled = async () => {

   let lines = readDataFromUserProvidedFile();
   const promises = Array.from({length:lines}).map(async (_,i) => {
      try {
         const result = await myFunc1(lines[i], i, i*2);
         console.log(`got result for line ${i}`,result)
         return result
      } catch(err) {
         console.log(`got an error for line ${i}`, err)
      }
   })

   const arrayOfResults = await Promise.all(promises)
   return arrayOfResults

}

相关问题