2

I have trouble understanding the whole concept of async/await and have spent hours reading the documentation and trying to get an example to work. But I still do not understand why this tiny example does not work.

The result I want is for document.body.innerHTML to contain the text "123", since doFirst() should finish running before doSomething() finishes.

But I always get "132", as if I haven’t used async/await at all. What am I doing wrong?

async function doFirst() {
  document.body.innerHTML = document.body.innerHTML + "1";
  setTimeout(function() {
    document.body.innerHTML = document.body.innerHTML + "2";
  }, 500);
}

async function doSomething()
{
  await doFirst();
  document.body.innerHTML = document.body.innerHTML + "3";
}

doSomething();

Sorry for posting this again, last time I posted this question it was marked as a duplicate. So this time I want to clarify that I’m not looking for any answers that show me how to do it properly but also for explanations on why my code does not work.

2
  • 2
    setTimeout is callback-based, not Promise-based. You need to convert it to a Promise so you can return it from doFirst, so that the doSomething can consume it as a Promise Commented Apr 11, 2020 at 8:20
  • 3
    @CertainPerformance, this user is looking for an explanation, not a "fix this code". This is not a duplicate of that question and it doesn't help them much. This should be re-opened. Commented Apr 11, 2020 at 8:34

3 Answers 3

4

That is because the doFirst() returns a promise that is resolved before the timer runs out. Contrary to your expectations, setTimeout does not pause script execution.

In order to ensure that doFirst() resolves after the timer runs out, you will need to wrap the entire function inside a Promise object. The timer will call the resolve() method once it runs out, as such:

function doFirst() {
  return new Promise(resolve => {
    document.body.innerHTML = document.body.innerHTML + "1";
    setTimeout(function() {
      document.body.innerHTML = document.body.innerHTML + "2";
      resolve();
    }, 500);
  });
}

async function doSomething()
{
  await doFirst();
  document.body.innerHTML = document.body.innerHTML + "3";
}

doSomething();

If you want to avoid callback hell, an cleaner way is to simply create a utility function that resolves a promise after a setTimeout, then you can await on the promise returned from that function to be resolved before printing "2" into the DOM:

// Utility function that resovles a promise after a given duration
function sleep(duration) {
  return new Promise(resolve => setTimeout(resolve, duration));
}

async function doFirst() {
  document.body.innerHTML = document.body.innerHTML + "1";
  await sleep(500);
  document.body.innerHTML = document.body.innerHTML + "2";
}

async function doSomething()
{
  await doFirst();
  document.body.innerHTML = document.body.innerHTML + "3";
}

doSomething();

Sign up to request clarification or add additional context in comments.

Comments

2

Take a look at below code:


async function doFirst() {
  document.body.innerHTML = document.body.innerHTML + "1";
  return myAsyncFunc(500)
}

function myAsyncFunc(delay) {
    return new Promise(resolve => setTimeout(function() {
            document.body.innerHTML = document.body.innerHTML + "2"
            resolve();
        }, delay));
}

async function doSomething()
{
  await doFirst();
  document.body.innerHTML = document.body.innerHTML + "3";
}

What are you doing wrong ?

You are not returning a promise in doFirst() , so the operation is not asynchronus (in short, your code doSomething() does not wait for doFirst() to finish up and move to line document.body.innerHTML = document.body.innerHTML + "3";)

Note:

  1. How document.body.innerHTML = document.body.innerHTML + "1"; is run immediately and the myAsyncFunc is returned (which basically returns a Promise).

  2. After 500ms , setTimeout executes and hence document.body.innerHTML = document.body.innerHTML + "2" is run. After that, the next line return resolve() is returned , which marks the Promise to be completed.

  3. The await is over as soon as resolve is returned and so the "3" is added

1 Comment

“You are now returning a promise in doFirst()[…]” – I think you mean “not” instead of “now” though that’s not true. doFirst does indeed return a promise, just one that does not wait for the timeout in order to resolve.
0

I think you may have a misconception about what the async keyword does. It does not magically change the function to be asynchronous, despite its name. It only does the following two things:

  1. Allow await to be used inside the function body.
  2. Ensure the function returns a Promise.

But only when those two are used in concert is it actually useful. An async function without await in its body does not need to be async at all.

Your example function doFirst does not use await, so it’s essentially equivalent to the following:

function doFirst() {
    document.body.innerHTML = document.body.innerHTML + "1";
    setTimeout(function() {
        document.body.innerHTML = document.body.innerHTML + "2";
    }, 500);
    return Promise.resolve(undefined);
}

As you can see, there’s nothing asynchronous about this code. Promise.resolve returns a promise that can immediately resolve to the given value. This means using await doFirst() in doSomething will not actually await anything as the promise is already resolved.

What you want for doFirst to do is return a promise that won’t resolve until the setTimeout callback has executed. Unfortunately setTimeout does not return a promise so you’ll need to create one manually:

function doFirst() {
    document.body.innerHTML = document.body.innerHTML + "1";
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            document.body.innerHTML = document.body.innerHTML + "2";
            resolve();
        }, 500);
    });
}

You may notice that you don’t need the async keyword here. That’s because the function does not use await and already returns a promise. doSomething will still be able to use await doFirst() because await works with any promise, not just when calling async functions.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.