Progress pill
Advanced JavaScript

Concurrency with async/await

JavaScript and NodeJS Fundamentals

Concurrency with async/await

  • What is async?
  • What is await?
  • Simulating a delay with await
  • Awaiting data
We saw how Promise chains help us avoid callback hell, but they can still get a little hard to read when there are many steps involved.
That’s where async and await come in. They let us write asynchronous code that looks like synchronous code, which makes it easier to understand.

What is async?

When you write the keyword async before a function, JavaScript automatically wraps the function’s return value in a Promise.
Let’s see a basic example:
async function greet() { return "hello" }
If you call this function:
const result = greet() console.log(result)
You’ll see this:
Promise { 'hello' }
Even though you just returned a string, JavaScript turns it into a Promise for you. You can get the actual value using .then() like this:
greet().then( result => console.log(result) ) // prints "hello"
Or you can use await...

What is await?

The keyword await tells JavaScript: “wait until this Promise is done, and then give me the result.”
But you can only use await inside an async function.
Let’s rewrite the example using await:
async function greet() { return "hello" } async function greetAndLog() { const result = await greet() console.log(result) } greetAndLog() // prints "hello"
Now we can use the result as if it was a regular value.
Let’s do something a little more useful now.

Simulating a delay with await

We’ll create a simple wait function that takes a quantity of milliseconds as argument and just resolves after that many milliseconds, without doing anything else:
function wait(ms) { return new Promise(resolve => { setTimeout(resolve, ms) }) }
Let’s try using it:
async function test() { console.log("waiting 2 seconds...") await wait(2000) console.log("done waiting") } test()
This prints:
waiting 2 seconds... done waiting
You can think of await as “pause here until the promise is done, then continue.”
This allows you to write code in a top-to-bottom fashion that behaves asynchronously, without chaining .then() calls.

Awaiting data

Let’s reuse our previous example, where we double numbers, then filter, then sum. But this time, we’ll use async/await.
We’ll create 3 functions that simulate waiting, and return Promises:
function doubleNumbers(numbers) { return new Promise(resolve => { setTimeout(() => { const doubled = numbers.map(n => n * 2) resolve(doubled) }, 1000) }) } function filterBigNumbers(numbers) { return new Promise(resolve => { setTimeout(() => { const filtered = numbers.filter(n => n > 3) resolve(filtered) }, 1000) }) } function sumNumbers(numbers) { return new Promise(resolve => { setTimeout(() => { const total = numbers.reduce((acc, n) => acc + n, 0) resolve(total) }, 1000) }) }
Now let’s write an async function to combine them:
async function process(numbers) { const doubled = await doubleNumbers(numbers) const filtered = await filterBigNumbers(doubled) const total = await sumNumbers(filtered) console.log("Final result:", total) } const input = [1, 2, 3] process(input)
This prints:
Final result: 10
This is much easier to read than chaining .then() or nesting callbacks.
It looks like a regular step-by-step program, but it still behaves asynchronously.