简体   繁体   中英

Why a promise reject is not catched within a try catch block but produces an Uncaught error?

I'm facing a promise rejection that escapes a try catch block. The rejection causes an Uncaught exception which is something I can not comprehend.

If I add a reject handler in the Promise.then or a Promise.catch, the rejection gets captured. But I was hopping try catch will work in this situation.

What is happening here?

 class HttpResponse { json() { return Promise.reject('parse error') } } function fetch() { return new HttpResponse(); } const res = fetch(); try { res.json().then(json => { alert(`json: ${json}`); } //,reason => { // alert(`promise.reject ${reason}`); //} ) //.catch(reason => { // alert(`promise.catch ${reason}`); //}) } catch (e) { alert(`catch{} from try catch: ${e}`); }

The technical reason behind this behavior is because a JavaScript promise invokes one of two callback functions for success or failure.

A promise does not emit an Error that is required for the try to work. it is not an Error (or more technically accurate) an instance of Error. It emits and event. You are encouraged to emit a new Error() if you need it to emit one. As pointed out here: https:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

It emits an event that you can set up a handler for: https://developer.mozilla.org/en-US/docs/Web/API/PromiseRejectionEvent

Finally, await throws and Error as described in the spec: https://tc39.es/ecma262/#await-rejected

Promises have their own mechanism of handling errors with the catch() method. A try / catch block can't control what's happening when chaining methods.

 function fetch() { return Promise.reject('parse error'); } const res = fetch(); res.then(json => { console.log(`json: ${json}`); }).catch((e) => { console.log(`catch{} from chained catch: ${e}`); });

However, using async / await changes that. There you don't use the methods of a promise but handle errors with a try / catch block.

 function fetch() { return Promise.reject('parse error'); } (async function() { try { const res = await fetch(); } catch (e) { console.log(`catch{} from try catch: ${e}`); } })();

Technical reasons behind:

HostPromiseRejectionTracker is an implementation-defined abstract operation that allows host environments to track promise rejections.

An implementation of HostPromiseRejectionTracker must complete normally in all cases. The default implementation of HostPromiseRejectionTracker is to unconditionally return an empty normal completion.

https://www.ecma-international.org/ecma-262/10.0/index.html#sec-host-promise-rejection-tracker

Basically javascript engines can freely implement this spec. In the case of browsers you don't get the Error captured inside the try/catch because the error is not emitted where you think it should be. But instead it's tracked with this special event that throws the error in the console. Also on nodejs, this event causes the process to exit if you have node set to exit on unhandled exceptions.

On the other side, if you instead use async/await, the rejection is treated like an 'error' in practical terms. Meaning that the newer async/await feature behaves in a different fashion showing that it is not only syntactic sugar for Promises.

https://tc39.es/ecma262/#sec-throwcompletion

In sum, if you use Promise.then you are forced to provide a reject argument or chain it with .catch to have the rejection captured or else it will reach the console and in case of nodejs to exit the process if configured to do so (I believe new nodejs does this by default). But if you use the newer async/await syntax you not only have a concise code (which is secondary) but a better rejection handling because it can be properly nested in a try/catch and it will behave like an standard Error.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM