Skip to main content

Asynchronous processing

A mechanism for progressing time-consuming processing (communication, timers, and so on) without stopping other processing.

What is asynchronous processing​

Asynchronous processing works with the flow of "once you start the processing, move on to the next without waiting for the result, and receive a notification when it completes."

Callback functions​

The most basic method, where you pass a function to be called after the processing completes.

function fetchData(callback) {
setTimeout(() => {
const data = { name: "Data", value: 42 };
callback(data);
}, 1000);
}

fetchData((data) => {
console.log("Data was fetched:", data);
});
console.log("Called fetchData"); // This line runs first
Callback hell

Nesting callbacks many levels deep results in hard-to-read code that gets deeper and deeper to the right (callback hell). Promises and async/await were introduced to solve this.

step1((result1) => {
step2(result1, (result2) => {
step3(result2, (result3) => {
console.log("Final result:", result3); // It gets deep and hard to read
});
});
});

Promise​

A Promise is an object that represents "a promise that a result will eventually be returned." It transitions through the following states.

const myPromise = new Promise((resolve, reject) => {
const success = true;
if (success) {
resolve("The processing succeeded!");
} else {
reject(new Error("An error occurred"));
}
});

myPromise
.then((result) => console.log(result)) // On success
.catch((error) => console.error(error)) // On failure
.finally(() => console.log("Processing complete")); // On either success or failure

Handling multiple Promises together​

const p1 = Promise.resolve("Success 1");
const p2 = Promise.reject("Failure");
const p3 = Promise.resolve("Success 2");

// Promise.all: returns an array if all succeed. If even one fails, the whole thing fails
Promise.all([p1, p3]).then((results) => {
console.log(results); // ['Success 1', 'Success 2']
});

// Promise.allSettled: waits for all of them. It doesn't stop even if there's a failure (ES2020)
Promise.allSettled([p1, p2, p3]).then((results) => {
console.log(results);
// [{ status: 'fulfilled', value: 'Success 1' }, { status: 'rejected', reason: 'Failure' }, ...]
});

// Promise.any: returns the first one that succeeds (ES2021)
Promise.any([p2, p1]).then((result) => {
console.log(result); // Success 1
});
Choosing between them
  • You want them all to succeed → Promise.all
  • You want all the results regardless of success or failure → Promise.allSettled
  • It's enough if any one succeeds → Promise.any

Async/Await (ES2017)​

Using async / await, you can write asynchronous processing in a straight line, like synchronous processing. Internally it uses Promises.

async function fetchUserData() {
try {
// Wait for the Promise's result with await
const response = await fetch("https://api.example.com/user");
const userData = await response.json();
console.log("User data:", userData);
return userData;
} catch (error) {
console.error("Failed to fetch data:", error);
throw error;
}
}

Serial and parallel​

// Serial: move to the next only after the previous await finishes (longer total time)
const a = await fetchA();
const b = await fetchB();

// Parallel: run them at the same time and wait for both (fast)
const [x, y] = await Promise.all([fetchA(), fetchB()]);
Execution order and microtasks

The processing you register with Promise.then() goes into a queue called the microtask queue, and it runs before normal tasks such as setTimeout. If you remember that things run in the order "synchronous code → the Promise's then → setTimeout," it becomes easier to predict the execution order of asynchronous code.

  • Best practices — How to write good code based on the knowledge so far