AWS Lambda
asynchronous execution
serverless computing
cloud functions
event-driven architecture

AWS Lambda async code execution

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Asynchronous execution in AWS Lambda can mean two related but different things: invoking a Lambda function asynchronously at the AWS service level, and writing non-blocking async code inside the function handler. Confusing those two ideas is common, and the design tradeoffs are different for each.

Asynchronous invocation at the Lambda service level

When Lambda is invoked asynchronously, the caller does not wait for the function result. Instead, the event is accepted by the Lambda service, queued, and then processed later.

This happens naturally for many event sources such as Amazon S3 and Amazon EventBridge. You can also request it directly when invoking Lambda through the API by setting the invocation type to Event.

bash
1aws lambda invoke \
2  --function-name processUpload \
3  --invocation-type Event \
4  --payload '{"bucket":"demo-bucket","key":"file.txt"}' \
5  response.json

With Event, the CLI call returns quickly and does not include the function's final business result. Lambda processes the event in the background.

What happens after an async invoke

For asynchronous invocations, Lambda queues the event, attempts execution, and can retry on certain failures. This is useful for workflows where durability matters more than an immediate response.

You can also configure destinations for success or failure, which makes it easier to connect async Lambda execution to queues, event buses, or alerting systems.

That makes async invocation a good fit for file processing, notifications, fan-out workflows, and background jobs.

Async code inside the handler

Separate from the invocation model, your handler code can also use asynchronous programming. In Node.js, that usually means async and await.

javascript
1export const handler = async (event) => {
2  const result = await Promise.resolve(`processed ${event.key}`);
3  return {
4    statusCode: 200,
5    body: result,
6  };
7};

This code is asynchronous from the runtime's perspective, but that does not automatically mean the Lambda invocation itself is asynchronous. If API Gateway invokes this function synchronously, the caller still waits for the returned response.

Putting the two ideas together

A Lambda function can be:

  • invoked synchronously while using async code internally
  • invoked asynchronously while still using async code internally
  • invoked asynchronously even if the handler itself is written in a synchronous style

That is why it helps to separate the transport-level question from the handler-implementation question.

Example: trigger background work from another Lambda

One Lambda can invoke another Lambda asynchronously so the first function finishes quickly.

python
1import boto3
2import json
3
4lambda_client = boto3.client("lambda")
5
6
7def handler(event, context):
8    lambda_client.invoke(
9        FunctionName="background-worker",
10        InvocationType="Event",
11        Payload=json.dumps({"task_id": event["task_id"]}).encode("utf-8"),
12    )
13    return {"status": "queued"}

Here the first function returns immediately after the request is handed to Lambda for background processing.

Failure handling matters in async flows

With synchronous invocation, the caller gets the error directly. With asynchronous invocation, the caller may already be gone by the time the function fails.

That means production-ready async Lambda designs usually need:

  • retry expectations
  • idempotent handlers
  • on-failure destinations or dead-letter queues
  • structured logging and tracing

Without those pieces, failed background work is easy to lose or duplicate.

Common Pitfalls

A common mistake is thinking async inside the handler automatically makes the Lambda invocation asynchronous. It does not. Invocation mode is decided by the event source or API call.

Another issue is ignoring retries. Async Lambda invocations may be retried, so side effects such as database writes or outgoing messages should be idempotent.

It is also easy to return before awaited work finishes. In Node.js especially, forgetting await can cause incomplete processing or confusing race conditions.

Summary

  • Lambda async execution can refer to async invocation, async handler code, or both.
  • Service-level async invocation means the caller does not wait for the final function result.
  • 'async and await inside the handler only describe how the runtime executes your code.'
  • Async workflows need explicit failure handling, retries, and idempotency.
  • Keep invocation model and handler implementation conceptually separate when designing Lambda systems.

Course illustration
Course illustration

All Rights Reserved.