Using Async/Await vs Promise.all with for-loops

Viewed 4033

Here is how I understand things currently:

async/await in a for-loop should pause execution until the promise resolves, meaning that the next iteration of the loop won't occur until that line finishes.

Consider the following array:

const data = [];
for (let i = 0; i <= 100000; i++) {
    data.push(i);
}

Method 1: Awaiting Promises in a for-loop

async function method1() {
  const startTime = new Date();
  console.log('start:', startTime);

  for (const item in data) {
    await new Promise(resolve => {
      if (item % 3 === 0) {
        resolve({});
      } else {
        resolve(item)
      }
    });
  }

  const endTime = new Date();
  console.log('finish:', endTime);
  console.log('total time:', endTime-startTime);
}

Because it requires sequential execution of promises to continue through the loop, I figured using Promise.all would be a performance enhancement that leverages asynchronous processing to a greater degree:

Method 2: Promise.all following a for-loop

async function method2() {
  const promises = [];
  const startTime = new Date();
  console.log('start:', startTime);

  for (const item in data) {
    const promise = new Promise(resolve => {
      if (item % 3 === 0) {
        resolve({});
      } else {
        resolve(item)
      }
    });

    promises.push(promise);
  }

  await Promise.all(promises);
  const endTime = new Date();
  console.log('finish:', endTime);
  console.log('total time:', endTime-startTime);
}

My reasoning: the loop would continue to bang out new Promises while each previously created Promise attempts resolve. So in my head, method 1 = blocking... while method 2 = less blocking.

When I run these both on repl.it, I found that method method 1 is actually quicker, almost by a factor of 2. Can somebody explain why this is? Shouldn't it be the other way around?

2 Answers

My guess is that you resolve too fast, so the looping dominates the execution time, since method2 has two loops: one to push to the array and one for promise.all to complete while method1 only has one loop, so your feeling "almost a factor of two" is actually theoretically correct.

Try faking an actual async operation like new Promise(resolve => setTimeout(resolve, 200)) and you should get what you expected

To add further to what Drake said. The only reason why method 2 is slower without timeouts is because of the number of loops it will have to encounter, but in a real scenario where request or asynchronous operations have delays. Method 2 will most of the time be faster

Here is the modified snippet with SetTimeout.

const data = [];
for (let i = 0; i <= 1000; i++) {
    data.push(i);
}


async function method1() {
  const startTime = new Date();
  console.log('start 1:', startTime);

  for (const item in data) {
   await new Promise(resolve => {
      setTimeout(()=>{
        if (item % 3 === 0) {
        resolve({});
      } else {
        resolve(item)
      }
      },1)
    });
  }
  const endTime = new Date();
  console.log('finish 1:', endTime);
  console.log('total time:', endTime-startTime);
}

async function method2() {
  const promises = [];
  const startTime = new Date();
  console.log('start 2:', startTime);

  for (const item in data) {
    const promise = new Promise(resolve => {
      setTimeout(()=>{
        if (item % 3 === 0) {
        resolve({});
      } else {
        resolve(item)
      }
      },1)
    });

    promises.push(promise);
  }

  await Promise.all(promises);
  const endTime = new Date();
  console.log('finish 2:', endTime);
  console.log('total time:', endTime-startTime);
}

method1()
method2()

Related