Mastering JavaScript Promises: Event Loop, Tasks, and Advanced Patterns
This article explains JavaScript’s event mechanism, distinguishes macro‑ and micro‑tasks, and provides a comprehensive guide to using Promises—including their states, API methods like then, catch, finally, and utilities such as all, race, with practical code examples and common pitfalls.
ES6's Promise object is a crucial point in asynchronous programming; this note shares a study guide, beginning with an overview of the JavaScript event mechanism and related asynchronous operations.
JS Event Mechanism
Before discussing the JS event mechanism, note that browsers run multiple processes. The JS engine lives in the rendering process and operates on a single thread, executing one task at a time. When a task takes too long, the UI can freeze. Asynchronous programming solves this single‑threaded bottleneck.
When the JS engine encounters an asynchronous event, it does not wait for the result; it suspends the event and continues executing other tasks on the call stack. Once the asynchronous event completes, its callback is placed into a separate queue—the event queue. The callback runs only after the current call stack is empty and the main thread is idle.
Macro Tasks & Micro Tasks
The event loop is a macro description; asynchronous tasks have different priorities and are divided into macro‑tasks and micro‑tasks.
Execution mechanism: 1) Execute a macro‑task (if none, fetch from the event queue). 2) If a micro‑task appears, add it to the micro‑task queue. 3) After the macro‑task finishes, run all micro‑tasks in order. 4) After the macro‑task completes, the GUI thread renders. 5) JS resumes and starts the next macro‑task.
Classic interview question:
setTimeout(() => { console.log(4); }, 0);
new Promise(resolve => {
console.log(1);
resolve();
console.log(2);
}).then(() => { console.log(5); });
console.log(3);
// Result: 1 2 3 5 4Promise is essentially an asynchronous micro‑task; let’s start the Promise journey.
Promise
Introduction
Promise is a solution for asynchronous programming that is more reasonable and powerful than traditional callbacks or events. It acts as a container holding the result of a future asynchronous operation. As an object, it returns a new Promise after each operation, supporting chaining and allowing asynchronous code to be expressed in a synchronous style.
Key Characteristics
Promise objects have three immutable states: pending (in progress), fulfilled (success), and rejected (failure). Only the outcome of the asynchronous operation determines the state.
Once a Promise’s state changes, it never changes again; the result is always available. The state transition is either pending → fulfilled or pending → rejected.
The state, once set, cannot be altered, and the result can be retrieved at any time.
Drawbacks
1. A Promise cannot be cancelled; it starts executing immediately upon creation.
2. Errors thrown inside a Promise are not automatically propagated if no callback is attached.
3. While pending, there is no way to know the progress of the operation.
Promise API
From the diagram, Promise is both an object and a constructor.
Basic Usage
resolve / reject
let promise = new Promise((resolve, reject) => {
if (/** operation succeeds */) {
resolve(success);
} else {
reject(error);
}
});The Promise constructor receives a function with resolve and reject parameters. Calling resolve fulfills the Promise; calling reject rejects it.
let p1 = new Promise((resolve, reject) => {
reject('error');
});
let p2 = new Promise((resolve, reject) => {
resolve(p1);
});
p2.then(s => console.log(s)).catch(e => console.log(e));then()
After a Promise is settled, then registers callbacks for the fulfilled and rejected states.
let promise = new Promise();
promise.then(success => {
// equivalent to resolve(success)
}, error => {
// handle rejection
}); then(onFulfilled, onRejected)returns a new Promise, enabling chainable calls and avoiding nested callbacks.
function createPromise(p, state) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (state === 0) {
reject(`error, ${p}`);
} else {
resolve(`success, ${p}`);
}
}, 0);
});
}
createPromise('p1', 1).then(s => {
console.log('111', s);
return createPromise('p2', 2);
}).then(s => {
console.log('222', s);
return createPromise('p3', 3);
}).then(s => {
console.log('333', s);
});
// 111 success, p1
// 222 success, p2
// 333 success, p3catch() catch is an alias for .then(null, onRejected), handling errors from either the fulfilled or rejected side.
new Promise().then(success => {
// ...
}).catch(error => {
// handle error
});finally() finally runs regardless of the Promise’s final state and does not receive any arguments.
createPromise('p1', 1).then(s => {
console.log('111', s);
}).catch(e => {
console.log('222', e);
}).finally(() => {
console.log('finally');
});
// 111 success, p1
// finallyall() Promise.all takes an array of Promises and resolves when all have fulfilled; if any reject, the returned Promise rejects with the first rejection reason.
Promise.all([createPromise('p1', 1), createPromise('p2', 1)])
.then(r => console.log(r)); // ["success, p1", "success, p2"]
Promise.all([createPromise('p1', 1), createPromise('p2', 0)])
.then(r => console.log(r))
.catch(e => console.log(e)); // error, p2If a Promise in all has its own catch, the rejection is handled there and all may still resolve.
let p2 = createPromise('p2', 0).catch(e => console.log('p2-catch', e));
Promise.all([createPromise('p1', 1), p2])
.then(r => console.log(r))
.catch(e => console.log(e));
// p2-catch error, p2
// ["success, p1", undefined]race() Promise.race resolves or rejects as soon as the first Promise in the iterable settles.
Promise.race([createPromise('p1', 1), createPromise('p2', 0)])
.then(r => console.log(r))
.catch(e => console.log(e));
// success, p1Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
MaoDou Frontend Team
Open-source, innovative, collaborative, win‑win – sharing frontend tech and shaping its future.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
