Represent a future outcome
A promise is pending, fulfilled or rejected. It settles once, and chained handlers observe or transform its outcome. The Promise constructor's executor runs synchronously; then/catch handlers run asynchronously after the current work.
function delay(milliseconds) {
return new Promise((resolve, reject) => {
if (!Number.isFinite(milliseconds) || milliseconds < 0) {
reject(new RangeError('Use a nonnegative delay'));
return;
}
setTimeout(() => resolve('Ready'), milliseconds);
});
}
delay(100)
.then(message => message.toUpperCase())
.then(message => console.log(message)) // READY
.catch(error => console.error(error.message))
.finally(() => console.log('Finished waiting'));Return from a chain
A then handler returns a value or another promise, producing the next link in the chain. If you start asynchronous work inside it but forget to return that promise, the outer chain can continue too early. Throwing in a handler rejects the resulting promise.
catch and finally
catch handles rejection. Returning a normal value from catch recovers the chain with fulfillment. finally is for cleanup and normally preserves the original result; throwing or returning a rejected promise there can replace it. Do not silently catch every error and pretend the requested operation succeeded.
Combine independent work
Promise.all([delay(10), delay(20)])
.then(results => console.log(results)); // ['Ready', 'Ready']Promise.all retains input order in its results, not completion order. It rejects when an input rejects but does not automatically cancel other work. Use allSettled when you need every outcome, and bound concurrency when a large list would overload a service.
Practice
- Call delay(-1) and confirm the rejection is handled.
- Log before delay and after registering then; predict handler timing.
- Chain a second delay by returning it from the first then.
- Explain why a promise is not the final value until its outcome is observed through a handler or await.