Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concurrent start with async/await

I thought I had a pretty good understanding of async await until I tried this:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('what'), 10000);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('?'), 15000);
});

async function asyncTest() {
  console.time();
  const p1 = await promise1;

  if (p1 === 'what') {
    var p2 = await promise2;
  }

  console.log(p1, p2);
  console.timeEnd();
}

asyncTest()

After 15000ms, asyncTest is logging p1 & p2. If instead promise1 and promise2 are transformed into functions that return these promises, execution time is then 25000ms. I have no idea what's going on. Could anyone care to explain this?

like image 987
L. Berger Avatar asked Jul 30 '18 12:07

L. Berger


People also ask

Is used to concurrent code using the async await?

Writing async code may be considered the first step for writing concurrent code. Since async/await is just a pattern, it doesn't make javascript any more concurrent or asynchronous than callbacks did. But it does make it exponentially easier to read it.

Is async await concurrent or parallel?

Concurrency is having two tasks run in parallel on separate threads. However, asynchronous methods run in parallel but on the same 1 thread.

Are async functions concurrent?

It is concurrent, in the sense that many outstanding asychronous operations may be in progress at any time. It may or may not be multithreaded. By default, await will schedule the continuation back to the "current execution context".

Does async await start a new thread?

The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active.


3 Answers

The issues is that your Promise callbacks are invoked immediately after you declare them.

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('what'), 10000);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('?'), 15000);
});

At this point, both callbacks have been invoked. 10000ms from this point, promise1 will resolve. 15000ms from this point, promise2 will resolve.

async function asyncTest() {
  console.time();
  const p1 = await promise1;

  if (p1 === 'what') {
    var p2 = await promise2;
  }

  console.log(p1, p2);
  console.timeEnd();
}

asyncTest()

At this point, as both callbacks have already been invoked, both will be resolved within 15000ms, which is what you observed.

In order to stretch this to the 25000ms you were expecting, try rewriting the Promises as follows:

const promise1 = () => new Promise((resolve, reject) => {
  setTimeout(() => resolve('what'), 10000);
});

const promise2 = () => new Promise((resolve, reject) => {
  setTimeout(() => resolve('?'), 15000);
});

Note these are now function expressions that will not be invoked immediately. Then rewrite your asyncTest function to invoke these expressions instead of simply await the already executing Promises:

async function asyncTest() {
  console.time();
  const p1 = await promise1();

  if (p1 === 'what') {
    var p2 = await promise2();
  }

  console.log(p1, p2);
  console.timeEnd();
}

This will force promise2 to wait until promise1 is completed before beginning execution.

like image 84
Fissure King Avatar answered Oct 29 '22 21:10

Fissure King


With the code in its current state:

You run setTimeout and assign a promise that will handle its result to promise1. Immediately after, you do the same thing for promise2.

So promise2 resolves about five seconds after promise1.


If you change the code so that promise1 and promise2 are functions, then:

You run setTimeout return a promise that will handle its result to await. It will wait until the promise resolves 10 seconds later. Then it will assign the result to p1.

Then (i.e. after 10 seconds have passed instead of immediately) it will do the same thing for p2.


In short:

In the first example, you don't wait for promise1 before setting promise2 going.

After making the change you describe, you do await promise1 before setting promise2 going.

like image 26
Quentin Avatar answered Oct 29 '22 19:10

Quentin


In the first example, two promises fire-off instantly at the beginning, and by the time when browser executes line var p2 = await promise2; the promise2 is on-half way to resolve and await statement pauses for 5000 ms.

console.log('promise1 created');
const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('what'), 10000);
});

console.log('promise2 created');
const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('?'), 15000);
});

async function asyncTest() {
  console.time();
  const p1 = await promise1;

  if (p1 === 'what') {
    var p2 = await promise2;
  }

  console.log(p1, p2);
  console.timeEnd();
}

asyncTest();

In the second example, you fire promises only when browser reaches the line where the function firing promise is called. So at line var p2 = await promise2(); the second promise is created and await statement pauses for 15000 ms. Thus total execution time is 25000 ms.

const promise1 = () => {
  console.log('promise1 created');
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve('what'), 10000);
  })
};


const promise2 = () => {
  console.log('promise2 created');
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve('?'), 15000);
  })
};

async function asyncTest() {
  console.time();
  const p1 = await promise1();

  if (p1 === 'what') {
    var p2 = await promise2();
  }

  console.log(p1, p2);
  console.timeEnd();
}

asyncTest();
like image 1
Olim Saidov Avatar answered Oct 29 '22 20:10

Olim Saidov