Node.js本机Promise.all是并行还是顺序处理?

我想澄清这一点,因为文档对此不太清楚。

问题1:Promise.all(iterable)按顺序还是并行处理所有承诺?或者,更具体地说,它相当于运行像

p1.then(p2).then(p3).then(p4).then(p5)....

或者是一些其他类型的算法的所有p1p2p3p4p5,等是被称为在同一时间(并行)和结果尽快返回所有的决心(或一个不合格品)?

问题2:如果Promise.all并行运行,是否有方便的方法可以依次运行可迭代程序?

注意:我不想使用Q或Bluebird,而是要使用所有本机ES6规范。

阿飞Sam2020/03/24 17:07:59

您可以通过for循环来实现。

异步函数返回承诺

async function createClient(client) {
    return await Client.create(client);
}

let clients = [client1, client2, client3];

如果您编写以下代码,则会并行创建客户端

const createdClientsArray = yield Promise.all(clients.map((client) =>
    createClient(client);
));

然后并行创建所有客户端。但是,如果要顺序创建客户端,则应使用for循环

const createdClientsArray = [];
for(let i = 0; i < clients.length; i++) {
    const createdClient = yield createClient(clients[i]);
    createdClientsArray.push(createdClient);
}

然后按顺序创建所有客户端。

快乐的编码:)

2020/03/24 17:07:59

我一直使用for来解决顺序承诺。我不确定这是否对您有帮助,但这就是我一直在做的事情。

async function run() {
    for (let val of arr) {
        const res = await someQuery(val)
        console.log(val)
    }
}

run().then().catch()
卡卡西Near2020/03/24 17:07:59

平行

看这个例子

const resolveAfterTimeout = async i => {
  return new Promise(resolve => {
    console.log("CALLED");
    setTimeout(() => {
      resolve("RESOLVED", i);
    }, 5000);
  });
};

const call = async () => {
  const res = await Promise.all([
    resolveAfterTimeout(1),
    resolveAfterTimeout(2),
    resolveAfterTimeout(3),
    resolveAfterTimeout(4),
    resolveAfterTimeout(5),
    resolveAfterTimeout(6)
  ]);
  console.log({ res });
};

call();

通过运行代码,它将为所有六个promise控制台“ CALLED”,并且在解决它们后,它将在超时后同时对每6个响应进行控制台

宝儿2020/03/24 17:07:59

NodeJS does not run promises in parallel, it runs them concurrently since it’s a single threaded event loop architecture. There is a possibility to run things in parallel by creating a new child process to take advantage of the multiple core CPU.

Parallel Vs Concurent

In fact, what Promise.all does is, stacking the promises function in the appropriate queue (see event loop architecture) running them concurrently (call P1, P2,...) then waiting for each result, then resolving the Promise.all with all the promises results. Promise.all will fail at the first promise which fail, unless you have manage the rejection yourself.

并行和并发之间的主要区别是,第一个将在完全相同的时间在单独的进程中运行不同的计算,并且它们将在此处进行节奏,而另一个将在不等待上一个的情况下依次执行不同的计算无需彼此依赖即可同时完成和进行计算。

最后,要回答您的问题,Promise.all将不会并行或顺序执行而不会并行执行。

路易Jim2020/03/24 17:07:59

使用async await,可以很容易地依次执行一个promise数组:

let a = [promise1, promise2, promise3];

async function func() {
  for(let i=0; i<a.length; i++){
    await a[i]();
  }  
}

func();

注:在上面的实现,如果承诺被拒绝,其余的将不会是executed.If要执行所有的承诺,那么你的包裹await a[i]();里面try catch

梅Harry米亚2020/03/24 17:07:59

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

该代码将运行数组中的每个项目,并在末尾添加下一个“ then链”。

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

希望这是有道理的。

泡芙2020/03/24 17:07:59

Bergi的答案帮助我使调用同步化。我在下面添加了一个示例,其中我们在调用前一个函数之后调用了每个函数。

function func1 (param1) {
    console.log("function1 : " + param1);
}
function func2 () {
    console.log("function2");
}
function func3 (param2, param3) {
    console.log("function3 : " + param2 + ", " + param3);
}

function func4 (param4) {
    console.log("function4 : " + param4);
}
param4 = "Kate";

//adding 3 functions to array

a=[
    ()=>func1("Hi"),
    ()=>func2(),
    ()=>func3("Lindsay",param4)
  ];

//adding 4th function

a.push(()=>func4("dad"));

//below does func1().then(func2).then(func3).then(func4)

a.reduce((p, fn) => p.then(fn), Promise.resolve());
达蒙2020/03/24 17:07:59

在平行下

await Promise.all(items.map(async item => { await fetchItem(item) }))

Advantages: Faster. All iterations will be executed even if one fails.

In sequence

for (let i = 0; i < items.length; i++) {
    await fetchItem(items[i])
}

Advantages: Variables in the loop can be shared by each iteration. Behaves like normal imperative synchronous code.

猪猪2020/03/24 17:07:59

您还可以使用递归函数使用异步函数顺序处理可迭代对象。例如,给定一个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"))

小卤蛋2020/03/24 17:07:59

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

不,诺言不能“被执行”。它们在创建时就开始执行任务-它们仅代表结果-并且在并行执行所有操作之前甚至将其传递给它们Promise.all

Promise.all只会等待多个承诺。它不在乎它们以什么顺序解析,也不管计算是否并行运行。

是否有一种方便的方法来依次运行迭代?

如果您已经有了承诺,那么您将无能为力,但是Promise.all([p1, p2, p3, …])(没有顺序的概念)。但是,如果您确实具有可迭代的异步函数,则实际上可以按顺序运行它们。基本上你需要从

[fn1, fn2, fn3, …]

fn1().then(fn2).then(fn3).then(…)

解决此问题的方法是使用Array::reduce

iterable.reduce((p, fn) => p.then(fn), Promise.resolve())
理查德Green2020/03/24 17:07:59

Bergis的答案使用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);
});

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

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);
});

在开始另一个文件传输之前没有等待传输完成,甚至在开始第一个文件传输之前就出现了“已传输所有文件”文本。

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

编辑:自从我写了这篇文章以来,我现在了解了为什么第一个版本不起作用。then()期望一个函数返回一个promise。因此,您应该传递不带括号的函数名称!现在,我的函数需要一个参数,因此我需要将一个不带参数的匿名函数包装起来!