Updated October 2026: Added the standard deferred
Promise.withResolvers()(ES2024), anasync/awaitversion, and fixed the jQuery example whose error branch could never run.
In JavaScript, Promise and Deferred are essential concepts used to handle asynchronous operations, such as fetching data from a server or performing time-consuming tasks. They provide a more readable and manageable way to work with asynchronous code, making it easier to handle asynchronous tasks and their results.
I. Promise
A Promise is an object representing the eventual completion or failure of an asynchronous operation. It can be in one of three states: “pending,” “fulfilled,” or “rejected.” Promises are commonly used to perform asynchronous actions and handle the results when the action is completed.
Example:
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const randomNumber = Math.random();
if (randomNumber > 0.5) {
resolve(randomNumber);
} else {
reject(new Error('Random number is too small!'));
}
}, 1000);
});
myPromise.then((result) => {
console.log('Resolved:', result);
}).catch((error) => {
console.error('Rejected:', error);
});
In the above example, a Promise is created to generate a random number and resolve or reject based on the value of that number. We use .then() to handle the resolution and .catch() to handle the rejection of the Promise.
The same consumer written with async/await, which is how most code handles promises today:
async function printRandom() {
try {
const result = await myPromise;
console.log('Resolved:', result);
} catch (error) {
console.error('Rejected:', error);
}
}
printRandom();
II. Deferred
Deferred is a design pattern used to create and manage Promises in a more flexible way: you get the promise and its resolve/reject functions as separate values, so the code that settles the promise does not have to live inside the new Promise(...) callback.
1. The standard way: Promise.withResolvers()
Since ES2024, JavaScript has a built-in deferred: Promise.withResolvers() returns an object { promise, resolve, reject }. MDN marks it as Baseline, available across browsers since March 2024.
Example:
function fetchData() {
const { promise, resolve, reject } = Promise.withResolvers();
setTimeout(() => {
if (Math.random() > 0.2) {
resolve({ message: 'Data fetched successfully!' });
} else {
reject(new Error('Network error'));
}
}, 1000);
return promise;
}
try {
const result = await fetchData(); // top-level await in a module, or inside an async function
console.log('Result:', result);
} catch (error) {
console.error('Error:', error);
}
Before ES2024, the usual workaround was to copy resolve/reject out of the executor:
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
A word of caution: a deferred is only useful when the code that settles the promise really is somewhere else (an event handler, a queue, a test helper). If you can do the work inside new Promise(...), or the API you call already returns a promise, use it directly. Wrapping an existing promise in a new promise or deferred (the “explicit construction” anti-pattern) adds code and easily swallows errors.
2. jQuery Deferred (legacy)
Libraries like jQuery provided a Deferred mechanism long before the standard one existed. You will still see it in older code; for new code, prefer native promises. Since jQuery 3.0, jQuery.Deferred is compatible with Promises/A+ and ES2015 promises.
Example (using jQuery Deferred):
function fetchData() {
const deferred = $.Deferred();
setTimeout(() => {
if (Math.random() > 0.2) {
deferred.resolve({ message: 'Data fetched successfully!' });
} else {
deferred.reject(new Error('Network error'));
}
}, 1000);
return deferred.promise();
}
fetchData().done((result) => {
console.log('Result:', result);
}).fail((error) => {
console.error('Error:', error);
});
In this example, we use $.Deferred() to create a Deferred object and return deferred.promise() to obtain a Promise from the Deferred object, so callers can listen to it but cannot resolve or reject it themselves. Then, we use .done() and .fail() to handle the result or error when the Promise is resolved or rejected. Note that the deferred must actually call reject() for the .fail() branch to ever run.
III. Conclusion
Promise and Deferred are fundamental concepts in JavaScript for handling asynchronous operations. Promise is the standard object for asynchronous actions, usually consumed with async/await. Deferred provides a more flexible approach to create and manage Promises: today it is built in as Promise.withResolvers(), while jQuery’s $.Deferred() belongs to older code. By understanding these concepts, developers can write cleaner and more efficient asynchronous code in JavaScript.
Public comments are closed, but I love hearing from readers. Feel free to contact me with your thoughts.