JavaScript - Promises
Overview
Estimated time: 35–45 minutes
Promises represent eventual results of asynchronous operations. Learn how to create, chain, and combine promises; handle errors correctly; and understand the microtask queue.
Learning Objectives
- Create and consume promises with
then
/catch
/finally
. - Chain promises and propagate values and errors predictably.
- Use combinators:
Promise.all
,allSettled
,race
, andany
. - Understand the microtask queue and timing with
queueMicrotask
.
Prerequisites
Creating promises
// A promise that resolves after ms
function delay(ms, value) {
return new Promise((resolve) => setTimeout(() => resolve(value), ms));
}
delay(100, 'ok').then(v => console.log(v)); // 'ok' after ~100ms
Chaining and error handling
delay(50, 2)
.then(x => x * 3) // 6
.then(x => { if (x > 5) throw new Error('too big'); return x; })
.catch(err => {
console.log('caught:', err.message); // 'too big'
return 0; // recover
})
.finally(() => console.log('done'))
.then(v => console.log('result:', v)); // 0
Microtasks and timing
console.log('A');
Promise.resolve().then(() => console.log('microtask'));
setTimeout(() => console.log('macrotask'), 0);
console.log('B');
// Order: A, B, microtask, macrotask
queueMicrotask(() => console.log('queued microtask'));
Combinators
const a = delay(30, 'A');
const b = delay(60, 'B');
Promise.all([a,b]).then(v => console.log('all:', v)); // ['A','B'] (after ~60ms)
Promise.allSettled([a, Promise.reject('X')]).then(console.log);
Promise.race([a,b]).then(v => console.log('race:', v)); // 'A' (first to settle)
Promise.any([Promise.reject('e1'), b]).then(v => console.log('any:', v)); // 'B'
Wrapping callback APIs
// Convert a Node-style callback function to a promise-based one
function toPromise(fn, ...args) {
return new Promise((resolve, reject) => {
fn(...args, (err, res) => err ? reject(err) : resolve(res));
});
}
// Example using setTimeout as a fake callback API
function cbTimeout(ms, cb) { setTimeout(() => cb(null, ms), ms); }
toPromise(cbTimeout, 20).then(v => console.log('cb -> promise:', v));
Common Pitfalls
- Missing return: Always return a value or promise in
then
handlers to chain correctly. - Throw vs reject: In
then
,throw
turns into a rejection; use whichever reads better. - Executor errors: Exceptions inside
new Promise(executor)
immediately reject. - Swallowed errors: End a chain with
catch
or.catch(console.error)
to avoid unhandled rejections.
Try it
Run to see chaining, errors, and combinators in action: