Promise.prototype.then()
The primitive everything else is built on, including await. Two facts carry most of its behaviour: it always returns a new promise, and returning a promise from the callback flattens instead of nesting.
Demo
then returns a new promise resolved with whatever the callback returns, so 5 becomes 10. The third case is the part people underestimate: a throw inside the callback does not escape as an exception — it becomes a rejection of the promise then returned, which only a catch AFTER this then will see. A second argument to the same then would not.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| onFulfilled | Function | no (identity) | Called with the resolved value. Its return value resolves the new promise; a promise returned here is flattened. Omitted, the value passes through. |
| onRejected | Function | no (rethrow) | Called if the ORIGINAL promise rejected. It does NOT see errors thrown by onFulfilled — that is the difference from a following catch. |
Return value
Promise — A NEW Promise resolving to whatever the callback returned. If the callback returns a promise, it is flattened rather than nested.
Common patterns
const v = await p; const t = transform(v);
fetch(u).then(r => r.json()).then(d => d.items);
p.then(transform).catch(handle);
Examples
Pitfalls
p.then(() => { throw new Error("x"); }, e => "handled")
p.then(() => { throw new Error("x"); }).catch(e => "handled")
p.then(v => { transform(v); }).then(t => t)
p.then(v => { return transform(v); }).then(t => t)
p.then(() => { save(); }).then(() => "done")
p.then(() => save()).then(() => "done")
let x; Promise.resolve(1).then(v => { x = v; }); x
const x = await Promise.resolve(1); x
When to use
- Interoperating with promise-returning APIs in non-async code
- Attaching a handler without making the surrounding function async
- Short transformation chains where await would need an extra function
- Ordinary sequential async logic → await, which reads far better
- Error handling → catch after, not the second argument
- You need every result of several promises → Promise.all
- The callback starts async work → return its promise
Notes
FAQ
Almost always the second. The two-argument form only handles rejections from the ORIGINAL promise, so an error in fn escapes it. A following catch covers both, which is what people expect from the name.
p.then(fn).catch(handler); // covers errors in fn too