anasqadrei
anasqadrei

Reputation: 525

What's the difference between Promise.all and adding promises using plus sign?

I'm trying to run few async functions in parallel/concurrently.

I found two solutions but don't know the difference between them. they either use Promise.all or addition symbol.

Also I don't see a point of checking the results if one function throws an exception.

await Promise.all([asyncFunction01(), asyncFunction02()])
const [p1, p2] = await Promise.all([asyncFunction01(), asyncFunction02()])
const p1 = asyncFunction01()
const p2 = asyncFunction02()
await p1 + await p2
const p1 = asyncFunction01()
const p2 = asyncFunction02()
const result = await p1 + await p2

They all seem run the same to me. They all run in parallel and fail fast in case of an error was thrown. I like the third option because it looks neater.

So what are the differences? Am I missing something?

Upvotes: 3

Views: 220

Answers (3)

axiac
axiac

Reputation: 72256

A Promise is an object that represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.

When a Promise object is created (when new Promise() returns), the asynchronous operation has already been started and it will run and complete with success or failure no matter if it is await-ed or not.

await-ing a Promise is just a way to accomplish these things:

  • synchronize the async operation with the code that launched it; without await the code that launches the async operation usually completes before the async operation; await pauses it until the async operation completes and resumes it afterwards, allowing the execution of multiple async operations in series (useful when a later operation depends on results produced by an operation that must run before it);
  • get the result of the async operation, be it a value (or no value at all) on success or an exception on error.

Regarding the asynchronous processing, await p1 + await p2 has the same results as await p1; await p2. The p1 promise is awaited to complete then the p2 promise is awaited to complete. If p2 completes before p1, await p2 returns immediately.

The purpose of Promise.all() is not to "run the promises in parallel". The promises do not run, they are just data (the results of the asynchronous code). The asynchronous code behind the promises runs and it runs "in parallel" because this is what asynchronous code does by its nature. It doesn't need Promise.all() to do that.

The purpose of Promise.all() is to produce a mechanism to easily await for all the wrapped promises and to capture the resolved values of all wrapped Promises into a single value (an array).

It also fails when one of the wrapped Promise objects fails.

For the completion/fail part, await p1 + await p2 is, more or less, equivalent to await Promise.all([p1, p2]).

Capturing and returning the resolved values of p1 and p2 is a different story.
await p1 + await p2 works only if the resolved values of p1 and p2 can be combined using the + operator. It doesn't work with arrays and objects. It doesn't work even with numbers when the results must be combined in a different way.

Upvotes: 1

Namaskar
Namaskar

Reputation: 2119

There isn't a difference! Just two alternative ways to achieve it.

return5 = async () => new Promise(
  resolve => setTimeout(resolve.bind(null, 5),250)
);

return8 = async () => new Promise(
  resolve => setTimeout(resolve.bind(null, 8),300)
);

g = async () => {
  console.time('await +');
  p1a = return5(); // async task started here
  p2a = return8(); // async task started here
  // waiting for them to all finish here and are printed
  console.log(await p1a + await p2a);
  console.timeEnd('await +');

  console.time('Promise all');
  // async tasks started immediately when the promises are passed
  // as well as waiting for their completion here
  const [p1, p2] = await Promise.all([return5(), return8()]);
  // results are here and printed sync
  console.log(p1 + p2);
  console.timeEnd('Promise all');
}
g();

Upvotes: 2

Cully
Cully

Reputation: 6975

Adding two awaited promises together may or may not run in parallel, depending on when you initialize them. If you initialize them in the addition statement itself, they will run in series; the first one runs, then when it's done, the second one runs. See:

const p1 = () => new Promise((resolve, reject) => {
  setTimeout(() => { 
    console.log('p1')
    resolve(200)
  }, 5000)
})

const p2 = () => new Promise((resolve, reject) => {
  setTimeout(() => { 
    console.log('p2')
    resolve(100)
  }, 3000)
})

async function start() {
  const result = await p1() + await p2()
  console.log(result)
  Promise.all([p1(), p2()])
}

start()

If adding the awaited promises together made them run in series, you'd see p2 finish before p1. That isn't the case. However, when you run them using Promise.all, you see that p2 finishes first.

As @Kaiido pointed out in the comments, the OP shows starting the promises before awaiting them to add together. In that case, they will run in parallel:

const P1 = () => new Promise((resolve, reject) => {
  setTimeout(() => { 
    console.log('p1')
    resolve(200)
  }, 5000)
})

const P2 = () => new Promise((resolve, reject) => {
  setTimeout(() => { 
    console.log('p2')
    resolve(100)
  }, 3000)
})

async function start() {
  const p1 = P1()
  const p2 = P2()
  const result = await p1 + await p2
  console.log(result)
}

start()

You'll see that p2 finishes before p1. So you're right, there is functionally no difference. So depending on your use case, they'll work exactly the same. Some thoughts though:

  1. I think Promise.all is more clear. You're explicitly signaling to other developers (and your future self) that you want those promises to run in parallel.

  2. With Promise.all you don't have to create variables for each promise. In many cases, Promise.all will be cleaner. Though in your example of adding the two results together, using Promise.all will probably not be any cleaner:

const result = await Promise.all([p1(), p2()]
  .then(([r1, r2]) => r1 + r2)

Upvotes: 3

Related Questions