Understanding the Async/Await Pattern
Simplify Asynchronous Programming with Readable Code

What is the Async/Await Pattern?

Async/Await is a programming pattern used to write asynchronous code in a more readable, sequential-like manner. It allows you to pause code execution until a task completes — such as reading a file, making an API call, or querying a database — without blocking the entire program.

Why Do We Need Async/Await?

When working with operations that take time (like fetching data from the internet), we don't want our entire program to wait. Instead, we want to:

  • Start the task
  • Move on with other work
  • Come back when the task is done

This is where async/await shines: it lets you "wait" for something to finish, without freezing the entire program.

How Does It Work?

The pattern is built on two core keywords:

  • async: Marks a function as asynchronous. It always returns a promise-like result.
  • await: Can only be used inside an async function. It pauses execution until the awaited task is complete.

Example 1: Waiting for a Task to Finish

Let’s simulate a delayed task using a wait function.

function wait(seconds):
    create a promise-like object
    after 'seconds', resolve it
    return the promise

async function performTask():
    print "Task started"
    await wait(2)
    print "Task completed after 2 seconds"
    
performTask()
Task started
(waiting 2 seconds...)
Task completed after 2 seconds

Q: Why not just use a normal function?

A: Normal functions can’t pause execution. They run from top to bottom. Async functions allow "pausing" with await, giving us more control.

Example 2: Chaining Asynchronous Steps

Suppose we have three tasks, and we want them to run in sequence.

function waitAndPrint(message, seconds):
    wait for 'seconds'
    print message

async function runInSequence():
    await waitAndPrint("First done", 1)
    await waitAndPrint("Second done", 2)
    await waitAndPrint("Third done", 1)

runInSequence()
First done
(wait 2 seconds)
Second done
(wait 1 second)
Third done

Q: What happens if I don’t use await?

A: Without await, all tasks would start at the same time. This is sometimes useful (parallelism), but not when order matters.

Example 3: Error Handling in Async/Await

Errors can occur during asynchronous operations. We use try/catch to handle them.

function fetchData():
    wait for 2 seconds
    throw error "Data fetch failed"

async function load():
    try:
        data = await fetchData()
        print data
    catch error:
        print "Caught error:", error

load()
Caught error: Data fetch failed

Q: Is async/await better than callbacks or promises?

A: It’s not always better, but it’s definitely cleaner for many use-cases. It helps avoid "callback hell" and makes code easier to read and maintain.

Best Practices

  • Always use await inside async functions
  • Use try/catch blocks to catch and handle errors properly
  • Don’t overuse await inside loops — prefer batching or parallel execution if possible

Summary

The async/await pattern brings clarity and control to asynchronous programming. It helps developers write cleaner, linear-looking code without blocking operations. Mastering this concept is essential for modern programming in environments where tasks like file I/O, API calls, or timers are frequent.

Mallikarjuna Mallisetty, author
About the author

Mallikarjuna Mallisetty

General programming

Mallikarjuna shares practical programming tutorials and foundational concepts designed to help developers learn by building and experimenting.

View LinkedIn profile ↗