Mastering JavaScript Promises: From Basics to Advanced Patterns

This article introduces JavaScript Promises, covers their history, demonstrates how to promisify callbacks, use Promise.all for parallel tasks, chain with then, handle execution order, and implement custom cancellation, providing clear code examples and best‑practice guidance.

Aotu Lab
Aotu Lab
Aotu Lab
Mastering JavaScript Promises: From Basics to Advanced Patterns

What Is a Promise?

A Promise is an abstract object that represents an asynchronous operation and provides methods to handle its eventual result, helping to avoid callback nesting and improve code readability. It is now part of the official ECMAScript standard.

History

The concept dates back to 1976. Early JavaScript libraries such as jQuery.Deferred introduced similar functionality, and the Promise/A+ specification later standardized the API. Native support arrived with ECMAScript 2015.

Common Usage Scenarios

1. Promisifying Native APIs

Many built‑in functions use callbacks; wrapping them to return a Promise simplifies usage. The following example converts setTimeout into a Promise‑based timer.

function timer(fn, time) {
    return function () {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                fn();
                resolve();
            }, time);
        });
    };
}

Promise.resolve()
    .then(timer(() => console.log('1'), 1000))
    .then(() => console.log('2'));
/* Output:
1
2 */

This pattern is essentially a form of currying: the original function is transformed into a new one that returns a Promise, reducing boilerplate and improving readability.

2. Using Promise.all for Parallel Tasks

When several asynchronous operations must finish before proceeding, Promise.all runs them in parallel and resolves with an array of results in the same order as the input promises.

function loadImg(src) {
    return new Promise((resolve, reject) => {
        const img = document.createElement('img');
        img.src = src;
        img.onload = () => resolve(img);
        img.onerror = err => reject(err);
    });
}

function showImgs(imgs) {
    imgs.forEach(img => document.body.appendChild(img));
}

Promise.all([
    loadImg('1.png'),
    loadImg('2.png'),
    loadImg('3.png')
]).then(showImgs);

Note that the resolved values preserve the order of the original promise array, regardless of which image loads first.

3. Chaining with then

Sequential asynchronous steps can be expressed as a chain of then calls. Each callback must return a Promise for the next step to wait for its completion.

function getUserInfo() {
    console.log('getUserInfo start');
    return new Promise(resolve => {
        setTimeout(() => {
            const userInfo = { name: 'adamchuan' };
            console.log('getUserInfo end');
            resolve(userInfo);
        }, 1000);
    });
}

function getGroupInfo(userInfo) {
    console.log('getGroupInfo start');
    return new Promise(resolve => {
        setTimeout(() => {
            const groupInfo = { name: 'jdc' };
            console.log('getGroupInfo end');
            resolve({ groupInfo, userInfo });
        }, 1000);
    });
}

function getTaskInfo({ groupInfo, userInfo }) {
    console.log('getTaskInfo start');
    return new Promise(resolve => {
        setTimeout(() => {
            const taskInfo = { name: 'rebuild' };
            console.log('getTaskInfo end');
            resolve(taskInfo);
        }, 1000);
    });
}

Promise.resolve()
    .then(getUserInfo)
    .then(getGroupInfo)
    .then(getTaskInfo);
/* Output order:
getUserInfo start → getUserInfo end →
getGroupInfo start → getGroupInfo end →
getTaskInfo start → getTaskInfo end */

If a then callback does not return a Promise, the chain receives an already‑fulfilled Promise, causing subsequent callbacks to run immediately and breaking the intended order.

Obtain userInfo Obtain groupInfo Obtain taskInfo Only a single value can be passed to the next then; to forward multiple pieces of data, wrap them in an array or object.

4. Interrupting or Cancelling a Promise Chain

The standard Promise API lacks a built‑in cancel method. A common workaround is to throw a custom signal and catch it later.

class BreakSignal {}

Promise
    .then(() => {
        // start
    })
    .then(() => {
        if (wantToBreakHere) {
            throw new BreakSignal(); // interrupt
        }
    })
    .then(() => {
        // code to skip
    })
    .catch(BreakSignal, () => {
        // handle interruption
    });

Because any thrown exception jumps to catch, a distinct signal class is needed to differentiate intentional breaks from genuine errors.

Conclusion

Using Promises makes asynchronous flow clearer and error handling more precise, though it introduces extra boilerplate. The upcoming async/await syntax (ES7) will further simplify these patterns, but until then Promises remain the primary tool for managing JavaScript async code.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

frontendJavaScriptprogrammingAsyncPromise
Aotu Lab
Written by

Aotu Lab

Aotu Lab, founded in October 2015, is a front-end engineering team serving multi-platform products. The articles in this public account are intended to share and discuss technology, reflecting only the personal views of Aotu Lab members and not the official stance of JD.com Technology.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.