Async/Await & Futures in Programming

Writing Non-Blocking Code That Performs Better and Reads Clean

In partnership with

As applications grow more interactive and connected, the need for responsive, efficient, and scalable systems becomes essential. That’s where non-blocking code comes in.

Instead of making your program wait (and waste time) while it fetches data or performs I/O operations, modern languages now offer tools like Futures, Promises, and async/await to keep your code flowing without freezing the thread.

In this issue of Nullpointer Club, we’ll break down what async programming is, why it matters, how Futures and async/await work, and when (and when not) to use them.

You found global talent. Deel’s here to help you onboard them

Deel’s simplified a whole planet’s worth of information. It’s time you got your hands on our international compliance handbook where you’ll learn about:

  • Attracting global talent

  • Labor laws to consider when hiring

  • Processing international payroll on time

  • Staying compliant with employment & tax laws abroad

With 150+ countries right at your fingertips, growing your team with Deel is easier than ever.

The Problem with Blocking Code

Let’s say your application needs to fetch data from a database, an API, or the file system. In traditional synchronous programming:

result = get_user_data()  # blocks until response is ready

During this time, your thread does nothing else. If you're running a web server or UI thread, this blocks the entire application—even if the actual wait time is just a few milliseconds.

Multiply this by thousands of users or requests, and your system becomes sluggish or unresponsive.

Enter: Non-Blocking Code

Non-blocking programming allows you to initiate a task and move on to other work while it completes in the background.

This is especially powerful for:

  • Web servers (handling multiple requests efficiently)

  • UI apps (keeping interfaces smooth)

  • Data pipelines and async APIs

Futures: A Promise for Later

A Future represents a value that may not be available yet, but will be—eventually.

Think of it like ordering food at a restaurant: you place the order (start a task), and your waiter will bring it when it’s ready (the future completes). Meanwhile, you can talk, check your phone, or order dessert (do other work).

Example in Java:

CompletableFuture<String> future = getDataAsync();
future.thenAccept(data -> System.out.println("Got: " + data));

You're not blocking while getDataAsync() runs. When the data arrives, thenAccept is called automatically.

Async/Await: Non-Blocking, But Readable

Futures are powerful—but chaining callbacks can quickly become messy. That’s where async/await shines. It allows developers to write non-blocking code in a synchronous style—clean, readable, and maintainable.

Here’s a simple Python example:

async def fetch_data():     
response = await http_get("https://example.com")     print(response) 

What’s happening here:

  • await tells Python: "Pause here, let other things run while this completes."

  • The function is non-blocking, even though it looks synchronous.

This makes it easier to follow control flow, handle errors, and write more intuitive async code.

How It Works Under the Hood

While it looks simple, behind the scenes async/await uses:

  • Event loops (like libuv in Node.js or asyncio in Python)

  • Coroutines that can pause and resume

  • Promises/Futures to track completion

The event loop watches for completed tasks and resumes your function when it’s ready. This allows concurrent execution without multithreading.

Language Support at a Glance

Language

Feature

Support

JavaScript

async/await, Promises

Native

Python

async def, await, asyncio

3.5+

Rust

async/.await, Futures

Stable (via tokio, async-std)

Java

CompletableFuture, Project Loom

Supported

Go

Goroutines (not async/await)

Built-in concurrency model

When to Use Async/Await

Great for:

  • API calls

  • File or network I/O

  • Server-side request handling

  • Parallelizing tasks efficiently

Not great for:

  • CPU-bound tasks (use threads or background workers instead)

  • Simple, linear scripts

  • Code that doesn’t require high concurrency

Common Mistakes to Avoid

  • Mixing sync and async improperly
    Example: calling await in a non-async function

  • Blocking the event loop
    Example: doing heavy computation inside an async function without offloading it

  • Error handling
    Use try/await or .catch() wisely—async code often fails silently if not handled

Final Thought

Async/await and Futures allow you to build fast, responsive apps without drowning in threads or callbacks. As systems scale and demands increase, non-blocking code isn’t just a bonus—it’s a necessity.

The good news? You don’t need to sacrifice clarity to gain concurrency. With async/await, performance and readability can go hand-in-hand.

Until next time,
Team Nullpointer Club
 

Reply

or to participate.