A promise is rejected by reject(reason), by a throw inside the executor or a then handler, or by Promise.reject. A rejection moves down the chain: each then with no onRejected passes it on unchanged, until a rejection handler runs.
catch(f)isthen(undefined, f). It returns a promise that fulfills with whateverfreturns, so the chain recovers. To keep failing, throw again.then(a, b)does not protecta: ifathrows,bis skipped. Acatchafter thethensees both.finally(f)runs on either outcome, with no arguments. Its return value is ignored and the original value or reason passes through. But iffthrows or returns a rejected promise, that rejection replaces the outcome.try/catchonly catches rejections around anawait. A throw in asetTimeoutor event callback cannot reject a promise; wrap that callback innew Promiseand callreject.
Unhandled rejection. A promise that is rejected with no handler attached by the time the host checks. The spec only defines a hook, HostPromiseRejectionTracker, called with "reject" for a rejection without handlers and with "handle" when a handler is added to an already rejected promise. Hosts decide what to do:
- Browsers (HTML spec): after the microtask queue drains, fire a cancelable
unhandledrejectionevent on the global withpromiseandreason.preventDefault()suppresses the console report. A handler attached later firesrejectionhandled. - Node: emits
process.on('unhandledRejection', (reason, promise)), andrejectionHandledfor late handlers. If you have no listener, the default modethrowraises it as an uncaught exception, so the process exits with code 1 (default since Node 15).
Treat it as a bug: end every chain with a catch, an await inside try, or return it to a caller that handles it.