From Callback Hell to Async Heaven
A Love Letter to setTimeout
(but it’s time to move on)
Section titled “A Love Letter to setTimeout (but it’s time to move on)”If you’re like many devs (me included, once), your journey into asynchronous JavaScript probably started with a setTimeout()
and a dream.
setTimeout(() => { console.log("Do something after 1 second...");}, 1000);
It’s simple. It works. But what happens when you need several steps, in order? This happens:
setTimeout(() => { console.log("Step 1"); setTimeout(() => { console.log("Step 2"); setTimeout(() => { console.log("Step 3"); }, 1000); }, 1000);}, 1000);
It becomes unreadable, hard to debug, and worse—hard to reuse.
🌈 Step 1: Promises to the Rescue
Section titled “🌈 Step 1: Promises to the Rescue”new Promise((resolve, reject) => { // async logic here});
A basic wait() function looks like this:
function wait(ms) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, ms); });}```
```jswait(1000) .then(() => { console.log("Step 1"); return wait(1000); // return new Promise }) .then(() => { console.log("Step 2"); return wait(1000); }) .then(() => { console.log("Step 3"); });
No more deeply nested callbacks—but still a bit verbose.
✨ Step 2: async/await — Cleaner, More Sane
Section titled “✨ Step 2: async/await — Cleaner, More Sane”You can use async/await as a cleaner syntax on top of Promises.
function wait(ms) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, ms); });}
async function runSteps() { await wait(1000); console.log("Step 1");
await wait(1000); console.log("Step 2");
await wait(1000); console.log("Step 3");}
runSteps();
This code reads like synchronous code but is still non-blocking.
🧠 Real-World Use Cases
Section titled “🧠 Real-World Use Cases”Whether you’re:
- Fetching from an API
- Waiting for an animation to finish
- Responding to a user event
- Running a queue of tasks in order
You should be using async/await
for clarity, error handling, and future you.
🔁 Bonus: Mixing return new Promise() with async functions
Section titled “🔁 Bonus: Mixing return new Promise() with async functions”function delayAndReturnValue(ms, value) { return new Promise((resolve) => { setTimeout(() => { resolve(value); }, ms); });}
async function doSomething() { const result = await delayAndReturnValue(1000, 'Done!'); console.log(result); // "Done!" after 1 second}
✅ Summary
Section titled “✅ Summary”Concept | Syntax Example | When to Use |
---|---|---|
Callback | setTimeout(() => {...}, 1000) | Simple, one-off delayed actions |
Callback Hell | Nested callbacks | Avoid—hard to manage |
Promise | return new Promise((res, rej) => {}) | When modernizing callback-based logic |
Chained Promise | .then().then() | When awaiting sequential tasks |
async/await | await wait() | Default for modern async workflows |
🧭 Final Thoughts
Section titled “🧭 Final Thoughts”You can start small. Take one function, wrap it in a Promise, then call it from an async function.
If you love setTimeout (and hey, we all do), just know: async/await doesn’t take it away—it just gives it a cleaner, saner structure.