Is Node.js native Promise.all processing in parall

2019-01-04 08:00发布

I would like to clarify this point, as the documentation is not too clear about it;

Q1: Is Promise.all(iterable) processing all promises sequentially or in parallel? Or, more specifically, is it the equivalent of running chained promises like

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

or is it some other kind of algorithm where all p1, p2, p3, p4, p5, etc. are being called at the same time (in parallel) and results are returned as soon as all resolve (or one rejects)?

Q2: If Promise.all runs in parallel, is there a convenient way to run an iterable sequencially?

Note: I don't want to use Q, or Bluebird, but all native ES6 specs.

8条回答
来,给爷笑一个
2楼-- · 2019-01-04 08:20

In parallel

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.

查看更多
Ridiculous、
3楼-- · 2019-01-04 08:26

You can do it by for loop.

async function return promise

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

let clients = [client1, client2, client3];

if you write following code then client are created parallelly

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

then all clients are created parallelly. but if you want to create client sequentially then you should use for loop

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

then all clients are created sequentially.

happy coding :)

查看更多
趁早两清
4楼-- · 2019-01-04 08:26

I've been using for of in order to solve sequential promises. I'm not sure if it helps here but this is what I've been doing.

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

run().then().catch()
查看更多
ら.Afraid
5楼-- · 2019-01-04 08:34

this might answer part of your question.

yes, you can chain an array of promise returning functions as follows... (this passes the result of each function to the next). you could of course edit it to pass the same argument (or no arguments) to each function.

function tester1(a) {
  return new Promise(function(done) {
    setTimeout(function() {
      done(a + 1);
    }, 1000);
  })
}

function tester2(a) {
  return new Promise(function(done) {
    setTimeout(function() {
      done(a * 5);
    }, 1000);
  })
}

function promise_chain(args, list, results) {

  return new Promise(function(done, errs) {
    var fn = list.shift();
    if (results === undefined) results = [];
    if (typeof fn === 'function') {
      fn(args).then(function(result) {
        results.push(result);
        console.log(result);
        promise_chain(result, list, results).then(done);
      }, errs);
    } else {
      done(results);
    }

  });

}

promise_chain(0, [tester1, tester2, tester1, tester2, tester2]).then(console.log.bind(console), console.error.bind(console));

查看更多
霸刀☆藐视天下
6楼-- · 2019-01-04 08:35

Bergis answer got me on the right track using Array.reduce.

However, to actually get the functions returning my promises to execute one after another I had to add some more nesting.

My real use case is an array of files that I need to transfer in order one after another due to limits downstream...

Here is what I ended up with.

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

As previous answers suggest, using:

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

didn't wait for the transfer to complete before starting another and also the "All files transferred" text came before even the first file transfer was started.

Not sure what I did wrong, but wanted to share what worked for me.

Edit: Since I wrote this post I now understand why the first version didn't work. then() expects a function returning a promise. So, you should pass in the function name without parentheses! Now, my function wants an argument so then I need to wrap in in a anonymous function taking no argument!

查看更多
趁早两清
7楼-- · 2019-01-04 08:36

Is Promise.all(iterable) executing all promises?

No, promises cannot "be executed". They start their task when they are being created - they represent the results only - and you are executing everything in parallel even before passing them to Promise.all.

Promise.all does only await multiple promises. It doesn't care in what order they resolve, or whether the computations are running in parallel.

is there a convenient way to run an iterable sequencially?

If you already have your promises, you can't do much but Promise.all([p1, p2, p3, …]) (which does not have a notion of sequence). But if you do have an iterable of asynchronous functions, you can indeed run them sequentially. Basically you need to get from

[fn1, fn2, fn3, …]

to

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

and the solution to do that is using Array::reduce:

iterable.reduce((p, fn) => p.then(fn), Promise.resolve())
查看更多
登录 后发表回答