Atomic Promise Execution
In this lesson, you will learn how to ensure that a sequence of asynchronous operations either completes entirely or restores the system to its original state if any step fails.
The Problem with Partial Success
When you perform multiple asynchronous tasks in a row—such as updating a user's database record and then charging their credit card—you face a risk: what happens if the first task succeeds but the second fails? If you leave the database updated while the payment fails, your system enters an "inconsistent state."
"Atomic" execution treats these steps as a single, indivisible block. If any part of the chain breaks, we must "roll back" the changes made by the previous steps to maintain data integrity.
Implementing a Rollback Pattern
To achieve atomicity, we track our changes as we go and execute a compensatory action (a "rollback") inside a catch block.
async function processOrder(order) {
let recordCreated = false;
try {
// Step 1: Create local database entry
await database.insert(order);
recordCreated = true;
// Step 2: External payment processing
await paymentGateway.charge(order.amount);
console.log("Transaction complete!");
} catch (error) {
console.error("Step failed, rolling back...", error);
// Rollback logic: Undo Step 1 if it was already performed
if (recordCreated) {
await database.delete(order.id);
}
throw new Error("Transaction aborted due to failure.");
}
}
In this example, if paymentGateway.charge fails, the catch block triggers. Because we kept track of recordCreated, we know exactly how to revert the system to the state it was in before the function started. By nesting these operations within a try/catch block and defining clear inverse operations, we ensure that the system remains stable even when external APIs or database calls fail unexpectedly.
Key Takeaway: Atomic Promise execution requires you to anticipate failure points and implement explicit "undo" logic for every successful operation that precedes a potential failure.