Node.js是原生的Promise.all并行或顺序处理吗?

Yan*_*hon 151 javascript node.js promise es6-promise

我想澄清这一点,因为文件不太清楚;

Q1:Promise.all(iterable)处理所有的承诺顺序或并行?或者,更具体地说,它是否相当于运行链式承诺

p1.then(p2).then(p3).then(p4).then(p5)....
Run Code Online (Sandbox Code Playgroud)

或者是一些其他类型的算法的所有p1,p2,p3,p4,p5,等是被称为在同一时间(并行)和结果尽快返回所有的决心(或一个拒绝)?

Q2:如果Promise.all并行运行,是否有一种方便的方式来运行可迭代的顺序?

注意:我不想使用Q或Bluebird,而是使用所有原生ES6规范.

Ber*_*rgi 223

正在Promise.all(iterable)执行所有承诺吗?

不,承诺不能"被执行".他们在创建任务时开始他们的任务- 他们只代表结果 - 甚至在传递它们之前并行执行所有事情Promise.all.

Promise.all等待多个承诺.它不关心它们解决的顺序,或者计算是否并行运行.

是否有一种方便的方法来运行可迭代的顺序?

如果你已经有了你的承诺,你就做不了多少Promise.all([p1, p2, p3, …])(但没有一个序列的概念).但是如果你有一个可迭代的异步函数,你确实可以按顺序运行它们.基本上你需要得到

[fn1, fn2, fn3, …]
Run Code Online (Sandbox Code Playgroud)

fn1().then(fn2).then(fn3).then(…)
Run Code Online (Sandbox Code Playgroud)

并且解决方案是使用Array::reduce:

iterable.reduce((p, fn) => p.then(fn), Promise.resolve())
Run Code Online (Sandbox Code Playgroud)

  • 在这个例子中,iterable 是一个返回你想要调用的 promise 的函数数组吗? (3认同)
  • 谢谢."执行"一词只是我的变形.关键是每个人都明白我在说什么:)但是谢谢你的澄清.`Array.reduce`是我问题的解决方案.谢谢! (2认同)
  • @SSHThis:它与`then`序列完全一样 - 返回值是最后一个`fn`结果的承诺,你可以将其他回调链接到那个. (2认同)

dav*_*ler 49

在平行下

await Promise.all(items.map(async item => { await fetchItem(item) }))
Run Code Online (Sandbox Code Playgroud)

优点:更快.即使一个失败,也会执行所有迭代.

按顺序

for (let i = 0; i < items.length; i++) {
    await fetchItem(items[i])
}
Run Code Online (Sandbox Code Playgroud)

优点:循环中的变量可以由每次迭代共享.表现得像普通的命令式同步代码.

  • 或者:`for(const item of items)等待fetchItem(item);` (4认同)
  • @david_adler 在并行示例优势中,您说**即使失败也将执行所有迭代**。如果我没有错,这仍然会很快失败。要更改此行为,可以执行以下操作:`await Promise.all(items.map(async item =&gt; { return await fetchItem(item).catch(e =&gt; e) }))` (2认同)
  • 在并行示例中,“await fetchItem(item)”中的“await”是否必要?为什么不 `await Promise.all(items.map(item =&gt; fetchItem(item)))` (2认同)

Adr*_*tti 18

NodeJS 不会并行运行 promise,而是并发运行它们,因为它是单线程事件循环架构。通过创建一个新的子进程来利用多核 CPU,可以并行运行事物。

并行与并发

事实上,什么Promise.all是将 promises 函数堆叠在适当的队列中(参见事件循环架构)并发运行它们(调用 P1、P2、...)然后等待每个结果,然后用所有的 promise 解析 Promise.all结果。Promise.all 将在第一个失败的承诺中失败,除非你自己管理了拒绝。

并行和并发有一个很大的区别,第一个会在不同的进程中同时运行不同的计算,它们会按照那里的节奏进行,而另一个会一个接一个地执行不同的计算,而不需要等待前一个计算同时完成和进行而不相互依赖。

最后,回答你的问题,Promise.all不会并行或顺序执行,而是并发执行。

  • 这个不对。NodeJS 可以并行运行。NodeJS 有一个工作线程的概念。默认情况下,工作线程数为 4。例如,如果您使用加密库对两个值进行哈希处理,则可以并行执行它们。两个工作线程将处理该任务。当然,您的 CPU 必须是多核才能支持并行性。 (8认同)
  • 迄今为止最好的答案。我很困惑像 Node.js 这样的单线程架构如何并行运行多个 Promise。非常感谢先生。PS 我知道工作线程是如何以及它们如何工作的,但是 Promise 是由 Node.js 事件循环本身解决的,而不是通过使用 libuv 来解决的。所以 Node.js 能做的最好的事情就是同时执行它们(promise)。 (4认同)
  • 是的,你说得对,这就是我在第一段末尾所说的,但我谈到了子进程,当然他们可以运行工人。 (3认同)

tka*_*rls 10

贝尔吉斯的回答使用Array.reduce让我走上了正确的轨道.

然而,为了实际让函数返回我的承诺一个接一个地执行我不得不添加一些嵌套.

我的真实用例是一系列文件,由于下游的限制,我需要一个接一个地传输这些文件...

这就是我最终的结果.

getAllFiles().then( (files) => {
    return files.reduce((p, theFile) => {
        return p.then(() => {
            return transferFile(theFile); //function returns a promise
        });
    }, Promise.resolve()).then(()=>{
        console.log("All files transferred");
    });
}).catch((error)=>{
    console.log(error);
});
Run Code Online (Sandbox Code Playgroud)

如先前的答案所示,使用:

getAllFiles().then( (files) => {
    return files.reduce((p, theFile) => {
        return p.then(transferFile(theFile));
    }, Promise.resolve()).then(()=>{
        console.log("All files transferred");
    });
}).catch((error)=>{
    console.log(error);
});
Run Code Online (Sandbox Code Playgroud)

在启动另一个文件之前没有等待传输完成,并且在启动第一个文件传输之前也发送了"所有文件传输"文本.

不知道我做错了什么,但想分享对我有用的东西.

编辑:自从我写这篇文章后,我现在明白为什么第一个版本不起作用.then()期望一个函数返回一个promise.所以,你应该传递没有括号的函数名称!现在,我的函数需要一个参数,所以我需要在一个不带参数的匿名函数中包装!


Mar*_*yer 6

您还可以使用递归函数通过异步函数按顺序处理可迭代对象。例如,给定一个a要使用异步函数处理的数组someAsyncFunction()

var a = [1, 2, 3, 4, 5, 6]

function someAsyncFunction(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("someAsyncFunction: ", n)
      resolve(n)
    }, Math.random() * 1500)
  })
}

//You can run each array sequentially with: 

function sequential(arr, index = 0) {
  if (index >= arr.length) return Promise.resolve()
  return someAsyncFunction(arr[index])
    .then(r => {
      console.log("got value: ", r)
      return sequential(arr, index + 1)
    })
}

sequential(a).then(() => console.log("done"))
Run Code Online (Sandbox Code Playgroud)


Tim*_*olo 5

只是为了详细说明@Bergi答案(非常简洁,但很难理解;)

此代码将运行数组中的每个项目并将下一个“then chain”添加到末尾:

function eachorder(prev,order) {
        return prev.then(function() {
          return get_order(order)
            .then(check_order)
            .then(update_order);
        });
    }
orderArray.reduce(eachorder,Promise.resolve());
Run Code Online (Sandbox Code Playgroud)