Last modified: September 13, 2025

This article is written in: πŸ‡ΊπŸ‡Έ

Asynchrony

Asynchronous programming is a technique used to achieve concurrency, where tasks can be executed independently without waiting for other tasks to finish. It allows for nonblocking behavior, in contrast to synchronous execution that waits for one task to complete before starting the next task.

Asynchronous programming is particularly useful for tasks that involve I/O operations, such as fetching data from a database, where waiting for the data retrieval could freeze the user interface.

Building Blocks of Asynchronous Programming

Asynchronous programming offers non-blocking execution, which is especially beneficial for I/O-bound operations. The two main pillars of this paradigm are the event loop and async functions.

Function vs Corutine

Function:                         Coroutine:
+-------------------+             +-------------------+
|       Start       |             |       Start       |
+-------------------+             +-------------------+
           |                                 |
           v                                 v
+-------------------+             +-------------------+
|   Execute Task    |             |   Execute Part 1  |
+-------------------+             +-------------------+
           |                                 |
           v                                 v
+-------------------+             +-------------------+
|       End         |             |       Pause       |
+-------------------+             +-------------------+
                                             |
                                             v
                                  +-------------------+
                                  |   Execute Part 2  |
                                  +-------------------+
                                             |
                                             v
                                  +-------------------+
                                  |       End         |
                                  +-------------------+

Event Loop

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                   β”‚  Code Runs  β”‚
                   β”‚  on Stack   β”‚
                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                         β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  Async Functions / Web APIs   β”‚
   β”‚  do background tasks, timers, β”‚
   β”‚  network calls, etc.          β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                         β–Ό
                   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   (Completion triggers callback)
                   β”‚  Callback   β”‚   β†’ placed into Task Queue
                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                         β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚           Task Queue          β”‚
   β”‚   (events, callbacks waiting) β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                (Event Loop Checks)
                         β–Ό
                   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                   β”‚  Call Stack β”‚ ←─ Pushed again
                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
                         └─────► [ Repeat Cycle... ]

Futures and Tasks

Asynchrony vs. Multithreading

Async gives you concurrency on one thread by not blocking; threads give you preemptive concurrency and, with multiple cores, true parallelism.

Legend: letters = task running on CPU; - = not running (blocked, waiting, or preempted)

I. Synchronous, single thread

single thread: AAAA BBBBBBBBBBBBBBBBBBBBBB CCCCCCCCCC

Tasks run to completion one after another; others wait.

II. Synchronous, multiple threads (OS preemption)

thread 1: AAAAAAAAAAAA------------------------------
thread 2: ------------BBBBBB------------------------
thread 3: ------------------CCCCCCCCCCCCCCCCCCCCCCCC

Each thread can be scheduled independently; with multiple cores, B and C may truly run in parallel.

III. Asynchronous, single thread (cooperative)

single thread: AAAA----BBBB----AA--CCC--AA--BBBB----CCCC

Tasks yield at awaits; the event loop runs whatever is ready. No parallel CPU workβ€”just efficient interleaving during I/O waits.

IV. Asynchronous + multiple threads (hybrid)

thread 1 (event loop): AAAAAA--A--A--C--A--B--C--A------
thread 2 (worker):     ---BBBBBBBB-----------------------
thread 3 (worker):     -----------CCCCCCCC--------------

An async event loop offloads blocking/CPU-heavy bits to a thread (or process) pool.

Quick comparison

Aspect Asynchrony Threads
Scheduling Cooperative (await/callbacks) Preemptive (OS time slices)
Best for Many I/O-bound tasks (network, disk) CPU-bound work; true parallelism
Cost Lightweight (no per-task stack) Heavier (stack, context switches)
Hazards Blocking the loop stalls everything Races, deadlocks, contention
Composition Futures/Promises, event loop Locks, atomics, queues, thread-safe libs

When to use what

Practical tips

Challenges and Considerations

Typical Applications

| Use case | Pattern summary | C++ (preferred) | Python (preferred) | Why this choice | Shutdown behavior |
|---|---|---|---|---|---|
| High-concurrency HTTP **client** (scraper/crawler) | Fire thousands of non-blocking requests with bounded concurrency | C++20 coroutines + Boost.Asio/Beast; semaphore to cap in-flight | asyncio + aiohttp + asyncio.Semaphore | Excellent overlap of network waits; minimal threads | Cancel pending tasks, close ClientSession, await gather(return_exceptions=True) |
| High-concurrency HTTP **server** (API) | Event-loop reactor accepts and services requests | Boost.Asio/Beast coroutines; strand for handler serialization | FastAPI/Starlette on uvicorn/hypercorn (async) | Scales with connections, low memory/ctx switches | Stop accepting, drain keep-alives, graceful shutdown hook, time-boxed cancel of tasks |
| WebSockets chat/broadcast | Long-lived duplex connections, fan-out messages | Asio coroutines + Beast websockets | websockets / aiohttp WS | Async fits many idle sockets efficiently | Close WS with codes, cancel producers, flush queues |
| Reverse proxy / gateway | Stream request/response bodies, back-pressure | Asio coroutines + Beast, async stream copy | aiohttp proxy / anyio streams | Zero-copy-ish streaming, flow control | Cancel copy tasks, half-close, drain, then close |
| Streaming pipeline (socket→transform→sink) | Staged coroutines linked by queues | Asio + coroutines + bounded queues | asyncio.Queue + tasks per stage | Back-pressure & simple composition | Send sentinels/cancel, drain queues, await tasks |
| DB access (async driver) | Pooled async connections, transactions | Driver-specific async APIs (e.g., ozo for PG) | asyncpg / databases / SQLAlchemy async | Avoid thread pools for I/O; better throughput | Close pools, finish in-flight txns, cancel long queries |
| Periodic jobs/heartbeats | while loop with async sleep and cancellation | co_await async_timer (Asio steady_timer) | asyncio.TaskGroup + asyncio.sleep() | Cheap timers; easy cooperative cancel | Respect cancel, final iteration optional, stop timers |
| RPC client with retries/timeouts | Issue calls with per-call timeout, jittered retry | Asio + timers + coroutines | asyncio.wait_for + retry (tenacity/hand-rolled) | Compose timeouts & retries declaratively | Cancel on shutdown, abort retries, close transports |
| Bulk cloud uploads/downloads | Many concurrent objects with windowing | Asio coroutines + HTTP/S3 libs | aiohttp/SDK async clients | Hide latency; cap bandwidth with semaphores | Finish in-flight or checkpoint parts; close sessions |
| Batched DNS resolution | Pipeline many lookups concurrently | Asio async resolver + coroutines | asyncio.getaddrinfo in threadpool or aiodns | Parallelize high-latency lookups | Cancel outstanding; cache results; close resolver |
| MQ consumers/producers | Async consume/ack/publish with flow control | Asio + AMQP/Kafka client coroutines | aio-pika, aiokafka | Natural fit for broker I/O | Stop consume, flush acks/publishes, close channels |
| GUI app non-blocking network I/O | Keep UI thread responsive | Qt + coroutines/Asio; integrate event loops | Qt + qasync + asyncio | Avoids UI stalls; fewer threads | Cancel tasks before closing UI; disconnect signals |
| IoT gateway (many devices) | Thousands of idle TCP/MQTT sessions | Asio coroutines + strands | asyncio-mqtt / asyncio sockets | Async handles massive concurrency cheaply | Unsubscribe, close sockets, persist offsets/state |
| Rate-limited API worker | Token bucket + bounded concurrency | Asio timers + custom bucket | anyio/asyncio + leaky/token bucket | Smooths bursts; avoids 429s | Flush queue, stop token refills, cancel waiters |
| Async file↔socket streaming | Non-blocking file read/write to sockets | io_uring / Asio + files (platform-dep.) | aiofiles + asyncio streams | Preserve event-loop responsiveness | Flush buffers, fsync if needed, close handles |
| Bulk email/SMS send | Many slow servers; pipeline SMTP/API calls | Asio + SMTP/HTTP libs | aiosmtplib / async provider SDKs | Great for I/O-bound fan-out | Drain queues, handle deferred failures, close clients |
| Health checks/monitoring fan-out | Probe many endpoints on schedule | Asio coroutines + timers | asyncio + aiohttp + gather | Concurrency with small footprint | Cancel probes on exit; aggregate partial results |
| Lightweight multiplayer/session server | Many small messages per client | Asio UDP/TCP coroutines | asyncio UDP/TCP protocols | Low latency, single-threaded logic | Kick clients, flush outbound, close transports |
| Metrics fan-in (statsd/OTLP) | Receive, batch, forward asynchronously | Asio + UDP/TCP, batch timers | asyncio datagrams + batch send | High throughput with batching | Flush batch, stop intake, close sockets |
| Async subprocess orchestration | Start many short jobs, stream stdio | Asio + Boost.Process + async pipes | asyncio.create_subprocess_exec | Avoid blocking; supervise easily | Terminate on timeout, drain pipes, await wait() |
| Webhook/event handler workers | Handle many webhooks concurrently | Asio HTTP server + task queue | FastAPI + background TaskGroup | Bursty, I/O-bound workloads | Stop intake, finish tasks (time-boxed), persist offsets |
| β€œFire-and-forget” background task | Task not waited but tracked | Coroutine + supervisor registry | asyncio.create_task + tracking | Keep app responsive, but retain control | Store task refs; cancel explicitly on shutdown |
| Bridging blocking call into async | Isolate blocking library call | Thread pool just for the call | asyncio.to_thread() | Keep loop unblocked without full rewrite | Await completion; cap worker threads; cancel if long |

Examples

Examples in C++

Asynchronous programming in C++ involves performing tasks without blocking the main thread, allowing other operations to continue in parallel. This can be particularly useful for I/O-bound or computationally intensive tasks. The C++ Standard Library provides facilities for asynchronous programming, including the std::async function, std::future, and std::promise.

Asynchronous Tasks with std::async

The std::async function runs a function asynchronously, returning a std::future that will eventually hold the result of the function.

#include <iostream>
#include <future>
#include <thread>

int compute(int x) {
    std::this_thread::sleep_for(std::chrono::seconds(2)); // Simulate a long computation
    return x * x;
}

int main() {
    std::future<int> future = std::async(std::launch::async, compute, 5);

    std::cout << "Doing other work while waiting for the result..." << std::endl;

    int result = future.get(); // Wait for the result
    std::cout << "Result: " << result << std::endl;

    return 0;
}

In this example, std::async launches the compute function asynchronously, allowing the main thread to continue executing other code. The future.get() method waits for the result and retrieves it once the computation is complete.

Using std::future and std::promise

std::future and std::promise provide a way to communicate between threads asynchronously. A std::promise object is used to set a value that will be available to a std::future object.

#include <iostream>
#include <future>
#include <thread>

void set_value(std::promise<int>& promise) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    promise.set_value(10); // Set the value to be retrieved by future
}

int main() {
    std::promise<int> promise;
    std::future<int> future = promise.get_future();

    std::thread t(set_value, std::ref(promise));
    
    std::cout << "Waiting for value..." << std::endl;
    int value = future.get(); // Wait for the value
    std::cout << "Value: " << value << std::endl;

    t.join();
    return 0;
}

In this example, the set_value function sets a value to the promise, which is then retrieved by the future in the main thread.

Asynchronous Waiting with std::future

The std::future object provides a way to wait for a result that is being computed asynchronously.

#include <iostream>
#include <future>
#include <thread>

int slow_add(int a, int b) {
    std::this_thread::sleep_for(std::chrono::seconds(3));
    return a + b;
}

int main() {
    std::future<int> future = std::async(std::launch::async, slow_add, 3, 4);

    while (future.wait_for(std::chrono::milliseconds(500)) != std::future_status::ready) {
        std::cout << "Waiting for the result..." << std::endl;
    }

    std::cout << "Result: " << future.get() << std::endl;
    return 0;
}

Here, future.wait_for periodically checks if the result is ready, allowing the main thread to perform other tasks while waiting.

Exception Handling with Asynchronous Operations

Exceptions thrown in asynchronous tasks are captured by std::future and can be re-thrown when future.get() is called.

#include <iostream>
#include <future>
#include <stdexcept>

int risky_task() {
    throw std::runtime_error("Something went wrong");
    return 42; // This line won't be reached
}

int main() {
    std::future<int> future = std::async(std::launch::async, risky_task);

    try {
        int result = future.get();
        std::cout << "Result: " << result << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Caught exception: " << e.what() << std::endl;
    }

    return 0;
}

In this code, the exception thrown in risky_task is caught in the main thread when future.get() is called.

Asynchronous Data Sharing with std::shared_future

A std::shared_future allows multiple threads to share the result of an asynchronous operation.

#include <iostream>
#include <future>
#include <thread>
#include <vector>

int compute_value() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 10;
}

int main() {
    std::shared_future<int> shared_future = std::async(std::launch::async, compute_value).share();

    std::vector<std::thread> threads;
    for (int i = 0; i < 5; ++i) {
        threads.emplace_back([shared_future, i]() {
            std::cout << "Thread " << i << ": " << shared_future.get() << std::endl;
        });
    }

    for (auto& thread : threads) {
        thread.join();
    }

    return 0;
}

In this example, the result of compute_value is shared among multiple threads using std::shared_future.

Asynchronous Continuations with std::future

Asynchronous continuations allow chaining of asynchronous operations.

#include <iostream>
#include <future>
#include <chrono>

int initial_task() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 5;
}

int next_task(int input) {
    return input * 2;
}

int main() {
    auto future = std::async(std::launch::async, initial_task)
                    .then([](std::future<int> fut) {
                        return next_task(fut.get());
                    });

    std::cout << "Result: " << future.get() << std::endl;
    return 0;
}

Here, initial_task runs asynchronously, and once it completes, next_task is executed with the result.

Performance Considerations and Best Practices

No. Filename Description
1 01_basic_async.cpp Create and start a basic asynchronous task
2 02_future_create_task.cpp Create a task using Future and run it asynchronously
3 03_future_read_result.cpp Read the result of a completed Future task
4 04_pause_resume.cpp Pause and resume asynchronous tasks
5 05_run_heavy_functions.cpp Execute heavy functions asynchronously
6 06_data_sharing_queue.cpp Share data between asynchronous tasks using a Queue
7 07_semaphore.cpp Control access to shared resources with a Semaphore
8 08_producer_consumer.cpp Implement a producer-consumer pattern asynchronously
9 09_fetch_parallel.cpp Fetch data in parallel using async tasks
10 10_mutex.cpp Use a Mutex to synchronize access to shared resources
11 11_barrier.cpp Synchronize multiple asynchronous tasks using a Barrier
12 12_async_generator.cpp Create and use asynchronous generators
13 13_async_server.cpp Implement an asynchronous server
14 14_distributed_computing.cpp Demonstrate distributed computing with async tasks

Examples in Python

In Python, asynchronous programming allows you to run code concurrently without creating new threads or processes. This is especially useful for I/O-bound tasks, such as network requests or file operations, where waiting for an operation to complete would otherwise block other code from running. Python's asyncio library provides the primary framework for writing asynchronous code using the async and await keywords.

Asynchronous Functions

An asynchronous function is defined using the async def syntax. These functions return a coroutine, which is an object representing the eventual result of the function.

import asyncio

async def print_message(message):
    print(message)

# Example usage
async def main():
    await print_message("Hello from an asynchronous function!")

asyncio.run(main())

In this example, print_message is an asynchronous function that prints a message. The main function calls it with await, indicating it will wait for the coroutine to complete.

Running Asynchronous Tasks

You can run multiple asynchronous tasks concurrently using asyncio.create_task() or by awaiting multiple coroutines.

import asyncio

async def print_message(message):
    await asyncio.sleep(1)
    print(message)

async def main():
    task1 = asyncio.create_task(print_message("Task 1"))
    task2 = asyncio.create_task(print_message("Task 2"))
    await task1
    await task2

asyncio.run(main())

Here, asyncio.create_task() schedules the coroutines to run concurrently. The await statements ensure that main waits for both tasks to finish.

Handling Concurrent I/O Operations

Asynchronous functions are ideal for I/O-bound tasks where blocking operations can slow down the program. For example, making HTTP requests.

import asyncio
import aiohttp

async def fetch(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()

async def main():
    url = "http://example.com"
    html = await fetch(url)
    print(html)

asyncio.run(main())

In this example, fetch uses aiohttp for asynchronous HTTP requests. This allows multiple requests to be handled without blocking the event loop.

Awaiting Multiple Tasks

You can run multiple coroutines concurrently using await asyncio.gather().

import asyncio

async def fetch_data(n):
    await asyncio.sleep(n)
    return f"Data {n}"

async def main():
    results = await asyncio.gather(
        fetch_data(1),
        fetch_data(2),
        fetch_data(3)
    )
    print(results)

asyncio.run(main())

asyncio.gather() waits for all the provided coroutines to finish and returns their results in a list.

Using Async Context Managers

Async context managers, defined with async with, are used to manage resources that require cleanup after use.

import asyncio
import aiofiles

async def write_to_file(filename, content):
    async with aiofiles.open(filename, 'w') as file:
        await file.write(content)

async def main():
    await write_to_file('example.txt', 'Hello, Async World!')

asyncio.run(main())

In this example, aiofiles is used for asynchronous file operations. The file is automatically closed after writing.

Exception Handling in Asynchronous Code

Handling exceptions in asynchronous code is similar to synchronous code, using try-except blocks.

import asyncio

async def may_raise_exception():
    await asyncio.sleep(1)
    raise ValueError("An error occurred!")

async def main():
    try:
        await may_raise_exception()
    except ValueError as e:
        print(f"Caught an exception: {e}")

asyncio.run(main())

Here, the exception raised in may_raise_exception is caught and handled in main.

Asynchronous Iteration

Asynchronous iterators and iterables allow for asynchronous looping, useful when dealing with streams or large datasets.

import asyncio

class AsyncCounter:
    def __init__(self, limit):
        self.limit = limit
        self.count = 0

    async def __aiter__(self):
        return self

    async def __anext__(self):
        if self.count < self.limit:
            await asyncio.sleep(1)
            self.count += 1
            return self.count
        else:
            raise StopAsyncIteration

async def main():
    async for number in AsyncCounter(3):
        print(number)

asyncio.run(main())

In this example, AsyncCounter is an asynchronous iterable that generates numbers with a delay.

Performance Considerations and Best Practices

No. Filename Description
1 01_basic_async.py Create and start a basic asynchronous task
2 02_future_create_task.py Create a task using Future and run it asynchronously
3 03_future_read_result.py Read the result of a completed Future task
4 04_pause_resume.py Pause and resume asynchronous tasks
5 05_run_heavy_functions.py Execute heavy functions asynchronously
6 06_data_sharing_queue.py Share data between asynchronous tasks using a Queue
7 07_semaphore.py Control access to shared resources with a Semaphore
8 08_producer_consumer.py Implement a producer-consumer pattern asynchronously
9 09_fetch_parallel.py Fetch data in parallel using async tasks
10 10_mutex.py Use a Mutex to synchronize access to shared resources
11 11_barrier.py Synchronize multiple asynchronous tasks using a Barrier
12 12_async_generator.py Create and use asynchronous generators
13 13_async_server.py Implement an asynchronous server
14 14_distributed_computing.py Demonstrate distributed computing with async tasks

Examples in JavaScript

Node.js is inherently designed for asynchronous programming, primarily due to its non-blocking I/O model. It uses an event-driven architecture, which allows for efficient handling of concurrent operations. Asynchronous code in Node.js can be written using callbacks, Promises, and the async/await syntax.

Asynchronous Programming with Callbacks

Callbacks are the traditional way of handling asynchronous operations in Node.js. A callback is a function passed as an argument to another function, which will be called once the operation is complete.

const fs = require('fs');

fs.readFile('example.txt', 'utf8', (err, data) => {
    if (err) {
        console.error('Error reading file:', err);
        return;
    }
    console.log('File contents:', data);
});

console.log('Reading file...');

In this example, fs.readFile reads a file asynchronously and calls the provided callback function when the operation completes, either with an error or the file's data.

Asynchronous Programming with Promises

Promises provide a more elegant way to handle asynchronous operations by avoiding the callback pyramid of doom (nested callbacks). A Promise represents a value that may be available now, or in the future, or never.

const fs = require('fs').promises;

fs.readFile('example.txt', 'utf8')
    .then(data => {
        console.log('File contents:', data);
    })
    .catch(err => {
        console.error('Error reading file:', err);
    });

console.log('Reading file...');

Here, fs.promises.readFile returns a Promise. The then method handles the resolved value, while catch handles any errors.

Asynchronous Programming with async/await

The async/await syntax in Node.js (available from ECMAScript 2017) is syntactic sugar over Promises, making asynchronous code look and behave more like synchronous code.

const fs = require('fs').promises;

async function readFile() {
    try {
        const data = await fs.readFile('example.txt', 'utf8');
        console.log('File contents:', data);
    } catch (err) {
        console.error('Error reading file:', err);
    }
}

readFile();
console.log('Reading file...');

In this example, async declares an asynchronous function, and await pauses the execution until the Promise is resolved, making the code more readable and maintainable.

Handling Multiple Asynchronous Operations

Node.js provides several methods to handle multiple asynchronous operations concurrently, such as Promise.all, Promise.race, Promise.allSettled, and Promise.any.

const fetch = require('node-fetch');

async function fetchData() {
    try {
        const [response1, response2] = await Promise.all([
            fetch('https://api.example.com/data1'),
            fetch('https://api.example.com/data2')
        ]);
        
        const data1 = await response1.json();
        const data2 = await response2.json();
        
        console.log('Data 1:', data1);
        console.log('Data 2:', data2);
    } catch (err) {
        console.error('Error fetching data:', err);
    }
}

fetchData();

In this example, Promise.all waits for all the promises to resolve before continuing. This is useful when you need to perform multiple independent asynchronous operations and handle their results together.

Error Handling in Asynchronous Code

Proper error handling is crucial in asynchronous programming to avoid crashes and ensure reliability.

async function getData() {
    try {
        let response = await fetch('https://api.example.com/data');
        if (!response.ok) {
            throw new Error(HTTP error! status: ${response.status});
        }
        let data = await response.json();
        return data;
    } catch (err) {
        console.error('Error:', err.message);
        // Additional error handling logic
    }
}

getData();

In this example, the error is caught using a try-catch block, allowing for graceful handling of potential failures.

Asynchronous Iteration with for-await-of

Node.js supports asynchronous iteration using for-await-of, which is useful for processing streams of data.

const fs = require('fs');
const readline = require('readline');

async function processLineByLine() {
    const fileStream = fs.createReadStream('example.txt');
    const rl = readline.createInterface({
        input: fileStream,
        crlfDelay: Infinity
    });

    for await (const line of rl) {
        console.log(Line from file: ${line});
    }
}

processLineByLine();

Here, for-await-of iterates over each line of a file asynchronously, making it easier to work with data that comes in pieces, like streams.

Avoiding Callback Hell

To avoid deeply nested callbacks (callback hell), use Promises or the async/await syntax. For example:

// Callback Hell Example
function doSomething(callback) {
    fs.readFile('file1.txt', 'utf8', (err, data1) => {
        if (err) return callback(err);
        fs.readFile('file2.txt', 'utf8', (err, data2) => {
            if (err) return callback(err);
            callback(null, data1 + data2);
        });
    });
}

// Using Promises or async/await
async function doSomethingBetter() {
    try {
        const data1 = await fs.readFile('file1.txt', 'utf8');
        const data2 = await fs.readFile('file2.txt', 'utf8');
        return data1 + data2;
    } catch (err) {
        console.error('Error:', err);
    }
}

Using Promises or async/await leads to cleaner and more maintainable code compared to deeply nested callbacks.

Performance Considerations and Best Practices

No. Filename Description
1 01_basic_async.js Create and start a basic asynchronous task
2 02_future_create_task.js Create a task using Future and run it asynchronously
3 03_future_read_result.js Read the result of a completed Future task
4 04_pause_resume.js Pause and resume asynchronous tasks
5 05_run_heavy_functions.js Execute heavy functions asynchronously
6 06_data_sharing_queue.js Share data between asynchronous tasks using a Queue
7 07_semaphore.js Control access to shared resources with a Semaphore
8 08_producer_consumer.js Implement a producer-consumer pattern asynchronously
9 09_fetch_parallel.js Fetch data in parallel using async tasks
10 10_mutex.js Use a Mutex to synchronize access to shared resources
11 11_barrier.js Synchronize multiple asynchronous tasks using a Barrier
12 12_async_generator.js Create and use asynchronous generators
13 13_async_server.js Implement an asynchronous server
14 14_distributed_computing.js Demonstrate distributed computing with async tasks

Summary

Here is a table comparing asynchronous programming features in C++, Python, and Node.js:

Concept C++ Python Node.js
Library Standard Library (std::future, std::promise, std::async)
Boost.Asio
asyncio Native support (callbacks, Promises)
Event Loop Depends on the library (e.g., Boost.Asio provides I/O context) asyncio event loop Event loop provided by the libuv library
Coroutine C++20 coroutines (co_await, co_yield) async def (coroutine functions) Async functions (async keyword)
Future std::future (contains the result of an asynchronous operation) asyncio.Future (contains the result of a coroutine) Promise (represents the eventual result of an async operation)
Await co_await (C++20) await (used inside async def) await (used inside async functions)
Async Call std::async or custom implementation with Boost.Asio asyncio.create_task(), asyncio.gather(), etc. Callbacks, Promises, or async/await syntax
Task std::packaged_task (wraps a callable target) asyncio.Task (wraps a coroutine) Promises, async functions, or callbacks

Table of Contents

    Asynchrony
    1. Building Blocks of Asynchronous Programming
      1. Function vs Corutine
      2. Event Loop
      3. Futures and Tasks
    2. Asynchrony vs. Multithreading
    3. Challenges and Considerations
    4. Typical Applications
    5. Examples
      1. Examples in C++
      2. Examples in Python
      3. Examples in JavaScript
    6. Summary