The Secret Life of JavaScript: The Promise

Published: (January 15, 2026 at 12:06 AM EST)
4 min read
Source: Dev.to

Source: Dev.to

Timothy stood at the chalkboard, his hand cramping. He had been writing code for ten minutes, but he was stuck in a corner—literally. His code had drifted so far to the right side of the board that he was squeezing letters against the wooden frame.

“I am trying to do three simple things,” Timothy complained. “Login, get the user’s data, and then get their posts. But the code looks like a staircase to nowhere.”

He stepped back to show his monstrosity:

login(user, function(token) {
    getUser(token, function(profile) {
        getPosts(profile.id, function(posts) {
            console.log("Finally got posts:", posts);
        }, function(err) {
            console.error("Error getting posts");
        });
    }, function(err) {
        console.error("Error getting user");
    });
}, function(err) {
    console.error("Error logging in");
});

Margaret walked over, holding a fresh eraser. “Ah,” she said. “The Pyramid of Doom.”

“It is unreadable,” Timothy said. “I get lost in the brackets. And handling errors is a nightmare.”

Margaret erased the entire triangle of code. “That is because you are relying on callbacks. You are handing control of your library to someone else and hoping they call you back.”

She drew a single, clean box on the board.

“It is time you learned about the Promise.”

The IOU (The Promise Object)

“A Promise,” Margaret explained, “is an object. It is not the value itself. It is a placeholder—an IOU.”

Pending: “I am working on it.”
Fulfilled: “Here is your data.”
Rejected: “Something went wrong.”

“Instead of passing a function into login, the login function returns a Promise object out to you. You can hold it, pass it around, and attach instructions to it.”

const loginPromise = login(user);

loginPromise.then(function(token) {
    // This runs only when the promise is fulfilled
    return getUser(token);
});

“Better,” Timothy admitted. “But if I have three steps, don’t I still nest them?”

“No,” Margaret said. “Because a Promise returns… another Promise. You can chain them flat.”

login(user)
    .then(token => getUser(token))
    .then(profile => getPosts(profile.id))
    .then(posts => console.log("Finally got posts:", posts))
    .catch(err => console.error("Something went wrong:", err));

Timothy traced the line. “It’s flat. And there is only one .catch() at the end?”

“Yes,” Margaret nodded. “Instead of handling errors at every single level, you catch them all in one place at the bottom. The Pyramid is gone.”

The Pause (Async / Await)

Timothy looked at the chain. “It is cleaner,” he agreed. “But it still looks… different. It doesn’t look like normal, top‑to‑bottom programming.”

“You are right,” Margaret smiled. “The chain is still a bit ‘syntactic.’ If you want code that looks truly human, you need async / await.”

She wiped the board again.

“Remember the event loop? Remember how we said you cannot block the stack?”

“Yes,” Timothy recited. “If I pause the stack, the browser freezes.”

“Correct. But await allows you to pause the function without blocking the stack.”

async function showUserPosts() {
    try {
        const token = await login(user);
        const profile = await getUser(token);
        const posts = await getPosts(profile.id);

        console.log(posts);
    } catch (error) {
        console.error("Something went wrong:", error);
    }
}

Timothy stared at it. “Line 3: await login(user). The code stops there? It waits for the network?”

“The function pauses,” Margaret corrected. “When the engine sees await, it suspends this function and effectively steps aside. It returns control to the rest of the browser.”

She drew a quick diagram of the stack.

“While this function is suspended, the browser stays alive—handling clicks and rendering styles. The stack is free.”

“And when the data comes back?”

“The rest of the function—the continuation—gets dropped into the microtask queue (the VIP line). As soon as the stack is empty, your function hops back on and resumes exactly where it left off, on line 4, with the data in hand.”

The Conclusion

Timothy looked at the three versions on the board:

  • The Pyramid (callbacks)
  • The Chain (promises)
  • The Story (async/await)

“They all do the same thing,” Timothy realized.

“They do,” Margaret said. “But the last one tells the truth. It lets you write code that looks synchronous—step by step, with a single try/catch block—while behaving asynchronously.”

She handed him the chalk.

“You don’t have to nest your logic anymore, Timothy. You just have to wait for it.”

Back to Blog

Related posts

Read more »

Middleware-Perfect-Symphony

GitHub Homehttps://github.com/hyperlane-dev/hyperlane Introduction Middleware is one of the most powerful concepts in web development, and also one of the most...

Introduction to WebAssembly (Wasm)

Unleash the Web's Inner Speed Demon: A Friendly Dive into WebAssembly Wasm Ever feel like your web browser, while amazing, is sometimes wrestling with heavy li...