2

I'm trying to understand JavaScript async/await. How can I rewrite the below such that the output is "Hi" then "Bye" instead of "Bye" then "Hi":

JSFiddle

sayHi() 
.then(sayBye);

async function sayHi() {
  await setTimeout(function() {
    $("#myOutput").text('hi');
  }, 1000);
}

async function sayBye() {
  $("#myOutput").text('bye');
}
Brad Larson
  • 170,088
  • 45
  • 397
  • 571
dikuw
  • 1,134
  • 13
  • 22
  • You can't `await` `setTimeout` because it doesn't return a `Promise`. – zero298 Jan 22 '18 at 18:13
  • Possible duplicate of [Combination of async function + await + setTimeout](https://stackoverflow.com/questions/33289726/combination-of-async-function-await-settimeout) – NH. Jan 22 '18 at 18:23

5 Answers5

4

In order to await setTimeout it needs to be wrapped into Promise. Then with async/await you can flatten your code write it without Promise then API:

(async () => { // await has to be inside async function, anonymous in this case
  await sayHi() 
  sayBye()
})()

async function sayHi() {
  return new Promise(function (resolve) {
    $("#myOutput").text('hi');
    setTimeout(function() {
      resolve()
    }, 1000)
  });
}

async function sayBye() {
  $("#myOutput").text('bye');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myOutput"></div>
dfsq
  • 191,768
  • 25
  • 236
  • 258
  • In this answer, why do the sayHi and sayBye functions themselves have to be async? It seems like your anonymous function is the only one that needs to be async. – Alex Mar 17 '21 at 14:48
1

setTimeout doesn't return a Promise. Create a helper function to wrap it in a Promise and then you can await it.

function delay(fn, t) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(fn());
        }, t);
    });
}

sayHi()
    .then(sayBye);

async function sayHi() {
    await delay(() => {
        //$("#myOutput").text('hi');
        console.log("Hi");
    }, 1000);
}

async function sayBye() {
    //$("#myOutput").text('bye');
    console.log("Bye");
}
zero298
  • 25,467
  • 10
  • 75
  • 100
  • For those who like arrow functions... `const delay = time => new Promise(res=>setTimeout(res,time));` – Cody G Jan 22 '18 at 18:23
1

Use the Promise way

sayHi() 
    .then(sayBye);

function sayHi() {
    return new Promise(resolve => {
        setTimeout(()=> {
            $("#myOutput").text('hi'), resolve()
        }, 1000);
    })
}

async function sayBye() {
    $("#myOutput").text('bye');
}

or the sayHi like this:

async function sayHi() {
    await new Promise(resolve => {
        setTimeout(()=> {
            $("#myOutput").text('hi'), resolve()
        }, 1000)
    })
}
Fernando Carvajal
  • 1,869
  • 20
  • 19
1

Using async/await is an excellent way to build acynchronous code in a quite controllable way. Promise based async function goes into microtasks depository, which event loop executes before events and methods contained in ordinary DOM refresh/web API depository (setTimeout() for example). However some versions of Opera and Firefox browsers set priority to setTimeout() over microtasks depository. Anyway, you can control order of execution if you combine Promise based function with async/await enabled function. For example:

// create function that returns Promise
let hi = () => {
  return new Promise((resolve, reject) => {
    setTimeout(_ => {
      resolve('Hi '); // after 1500ms function is resolved and returns 'Hi '
    }, 1500);
  });
}

// create ordinary function that will return 'bye'
let sayBye = () => {
  return 'Bye';
}

// create third function that will be async 'enabled', 
// so it can use await keyword before Promise based functions
let sayHi = async () => {
  let first = await hi();  // we store 'Hi ' into 'first' variable
  console.log(first);
  let second = sayBye(); // this execution will await until hi() is finished
  console.log(second);
}

// execute async enabled function
sayHi();

We can add try / catch block inside sayHi() function for controlling error on promise reject(), but this is out of scope of your question.

Have a nice day!

Dan
  • 640
  • 2
  • 8
  • 13
0

You cannot use async/await for functions that are not returning Promise

When an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value. When the async function throws an exception or some value, the Promise will be rejected with the thrown value.

An async function can contain an await expression, that pauses the execution of the async function and waits for the passed Promise's resolution, and then resumes the async function's execution and returns the resolved value.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

Usually it is used to handle data that comes from server, because when you have multiple queries it can override previous one and you will handle the wrong one.

Async/await lets you handle exactly data you are awaiting for.

Community
  • 1
  • 1
Sergey
  • 7,184
  • 13
  • 42
  • 85