Home > other >  Await keyword in Javascript async function is not resolving promises in sequential order
Await keyword in Javascript async function is not resolving promises in sequential order

Time:12-05

I am new to Javascript and trying to learn promises and async/await concepts. I created three promises as shown below.

const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("promise1");
        resolve("1");
    }, 1000)

});

const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("promise2");
        resolve("2");
    }, 5000)

});

const promise3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("promise3");
        resolve("3");
    }, 4000)

});

I have created a async function which is using await for multiple promises sequentially as shown below.


async function example() {


    const result1 = await promise1;
    const result2 = await promise2;
    const result3 = await promise3;
    console.log(result1);
    console.log(result2);
    console.log(result3);
}

example();

The output in browser console is -

promise1
promise3
promise2
1
2
3

I am not understanding why in my output promise3 is coming before promise2 because in sequence of await statements inside async function example, promise2 comes before promise3? According to me the output should be like below -

promise1
promise2
promise3
1
2
3

Please correct me if I am missing something or if there is any error.

CodePudding user response:

const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("promise1");
        resolve("1");
    }, 1000)
});

When you construct a promise, the code inside it runs immediately. So as soon as this line is done, a timer is off and running. Your code creates the 3 of these promises up front, which means it starts all 3 timers up front.

Later on when you await the promise, that does not change what the timers are doing. It just lets your async function know when they are done. So in the background, the timers will start going off, logging things out, and resolving their corresponding promises. This can happen even if nothing is awaiting the promise. The timeout of 1000ms will be the first to go off, followed 3 seconds later by the one of 4000ms, and then 1 second later the one of 5000ms

If you want the timers to only start once you get to that line of the async function, then you need to do the setTimeouts at that line of the async function. For example:

async function example() {
  const result1 = await new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("promise1");
      resolve("1");
    }, 1000);
  });
  const result2 = await new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("promise2");
      resolve("2");
    }, 5000);
  });
  const result3 = await new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("promise3");
      resolve("3");
    }, 4000);
  });
  console.log(result1);
  console.log(result2);
  console.log(result3);
}

CodePudding user response:

That's because you're creating the Promises outside of the async function without awaiting. The callback inside the Promise is syncrounos. You run the 3 setTimeout's one after another. Example:

const x = new Promise(resolve => console.log(1))
console.log(2)

This logs out 1,2 - as opposed to asyncrounous callbacks ex. in setTimeout's:

setTimeout(() => console.log(1));
console.log(2)

This logs out 2,1.

So, if you want the correct behaviour you'd have to create the Promises when you await for them, and that's common practice:

function makePromise(time, number) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("promise", number);
      resolve(number);
    }, time);
  });
}


async function example() {
    const result1 = await makePromise(1000, 1);
    const result2 = await makePromise(5000, 2);
    const result3 = await makePromise(4000, 3);
    console.log(result1);
    console.log(result2);
    console.log(result3);
}

example();

CodePudding user response:

In a nutshell, it's because you first create those three promises and in the process of creating those three promises, you also start your three timers. Those three timers are all started and are all running in parallel.

Then, you do await promise1. But, that statement has nothing at all to do with when the timers call their callback. They will do that entirely upon their own. Thus, the timer callbacks themselves create console.log() output according to the expected time each timer is set for (nothing at all to do with the await you have).

So, it happens this way because your first create all three promises and timers and THEN and only then, do you do an await. An important thing to understand here is that "promises don't execute". They aren't the asynchronous operation themselves. As soon as you do new Promise(), it calls the promise executor function and the asynchronous operation you have in there is started. What the promise does from then on is monitor that asynchronous operation and then notify observers when that asynchronous operation completes.

You can see more detail on the order of things if you add logging for when each timer is started which will show that all three timers are initially started and running in parallel and can call their timer callbacks completely independent of your await statements later in the code:

const promise1 = new Promise((resolve, reject) => {
    console.log("starting timer 1");
    setTimeout(() => {
        console.log("promise1");
        resolve("1");
    }, 1000)

});

const promise2 = new Promise((resolve, reject) => {
    console.log("starting timer 2");
    setTimeout(() => {
        console.log("promise2");
        resolve("2");
    }, 5000)

});

const promise3 = new Promise((resolve, reject) => {
    console.log("starting timer 3");
    setTimeout(() => {
        console.log("promise3");
        resolve("3");
    }, 4000)

});

async function example() {
    const result1 = await promise1;
    const result2 = await promise2;
    const result3 = await promise3;
    console.log(result1);
    console.log(result2);
    console.log(result3);
}

example();
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

If you changed the structure of your code so that a function you were awaiting is what actually creates and starts the timers, then you wouldn't be starting the second timer until after the first timer fires and you'd have sequential timers.

So, your expected output would happen if you did this instead:

function runTimer1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("promise1");
            resolve("1");
        }, 1000)

    });
}

function runTimer2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("promise2");
            resolve("2");
        }, 5000)

    });
}

function runTimer3() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("promise3");
            resolve("3");
        }, 4000)

    });
}

async function example() {
    const result1 = await runTimer1();
    const result2 = await runTimer2();
    const result3 = await runTimer3();
    console.log(result1);
    console.log(result2);
    console.log(result3);
}

example();
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related