Define both outcomes
A simulated delivery lookup can supply a charge or fail. A Promise represents eventual fulfillment with a value or rejection with a reason. While neither has happened it is pending. Once settled, that promise does not switch outcomes.
function lookupDeliveryCharge() {
return Promise.resolve(5);
}
lookupDeliveryCharge()
.then((charge) => {
console.assert(charge === 5);
return 40 + charge;
})
.then((total) => {
console.assert(total === 45);
console.log("Quote:", total);
});
Promise.resolve creates a fulfilled promise here, but then callbacks still run asynchronously after current synchronous work. Then returns a new promise whose outcome follows the callback's returned value or thrown error. Returning the computed total passes it to the next step; merely logging it would not.
Guided failure path
Promise.reject(new Error("Charge unavailable"))
.catch((error) => {
console.assert(error.message === "Charge unavailable");
console.log("Cannot produce the quote yet");
});
Catch handles a rejection. If it returns a normal value, the resulting chain becomes fulfilled with that value. Returning 0 here would imply a free charge unless explicitly modeled otherwise. Handle failure honestly.
Creating a Promise does not itself make expensive synchronous work nonblocking. The executor of new Promise(...) runs synchronously; it is usually used to adapt a callback-based operation, not to wrap every calculation.
Independent exercise
Extend the first chain to format the quote as text in one additional then. Predict the value received at every stage. What if the first callback forgets its return?
Correction
Stages receive charge 5, total 45 and then the formatted text if each callback returns it. Without the first return, the next callback receives undefined. The promise infrastructure cannot infer the missing data flow.
Review
A promise is not the final amount. Use then or, in the next lesson, await to express work that depends on fulfillment. Always account for how rejection reaches a handler.