简体   繁体   中英

How can I catch an asynchronous error using JS promises?

Is it possible to catch asynchronous errors using the ES6 .catch syntax of promises? For example, the following doesn't work (the .catch doesn't catch the error):

new Promise((resolve, reject)=>{
    setTimeout(()=>{throw new Error("uh oh")}, 1);
}).then(number=>{
    console.log("Number: " + number);
}).catch(e=>{
    console.log("Error: " + e);
});

But this synchronous version does:

new Promise((resolve, reject)=>{
    throw new Error("uh oh");
}).then(number=>{
    console.log("Number: " + number);
}).catch(e=>{
    console.log("Error: " + e);
});

Is the only solution to do something like the following, using a try/catch block and reject ing the error in the catch?

new Promise((resolve, reject)=>{
    try {
        setTimeout(()=>{throw new Error("uh oh")}, 1);
    }
    catch(e) {
        reject(e);
    }
}).then(number=>{
    console.log("Number: " + number);
}).catch(e=>{
    console.log("Error: " + e);
});

For the sake of this question, assume the part of the code that is throwing the Error is in another named function, so it doesn't have access to the reject function.

Thanks!!

Edit: Here is a more complete example of what I'd like to do, in JSFiddle.

Use resolve() , reject() within Promise constructor. Handle error at either onRejected or .catch() .

Note, once error is handled, onFulfilled at chained .then() , if any, should be reached, unless throw is used within onRejected or .catch() , to explicitly pass error to chained .then(_, onRejected) or .catch()

 function fn() { throw new Error("uh oh") } new Promise((resolve, reject) => { setTimeout(() => { try { resolve(fn()) } catch (e) { reject(e) } }, 1); }).then(number => { console.log("Number: " + number); }, e => { console.log("Error: " + e); }); 

There isn't a way to catch an error thrown like your first example. The problem here is that you are using the Explicit Promise Construction Antipattern. You are trying to have the Promise constructor do more than it needs to do.

Instead, you should promisify the smallest amount of asynchronous functionality and build on top of that. In this case, that would involve producing a promise that waits for a certain amount of time before resolving. Most 3rd party promise libraries already have a .delay() method, but it's very easy to create your own:

let delay = duration => new Promise(resolve => setTimeout(resolve, duration));

Then you can build on top of that, and catch the error easily:

 let delay = duration => new Promise(resolve => setTimeout(resolve, duration)); delay(1) .then(() => { throw new Error("uh oh"); }) .then(number => { console.log("Number: " + number); }).catch(e => { console.log("Error: " + e); }); 

"For the sake of this question, assume the part of the code that is throwing the Error is in another named function, so it doesn't have access to the reject function." – Christopher Shroba

"does this (non-existent in your code) function return a Promise?" – Jaromanda X

"Yes, the other function returns a promise normally, but because an asynchronous function inside that function is throwing an Error , the entire function is throwing an Error." – Christopher Shroba

Well next time post your code, because your ability to describe the problem with English will never be as good as actual code. By "asynchronous function" do you mean a function that returns a promise? If so ...


It doesn't matter how deep the errors throw in your Promises. Here's an example function three which calls a function two which calls a function one which has potential to throw an Error in the event the JSON is formed poorly. Each step makes a valuable contribution to the final computation, but in the event one throw an error, it will bubble up through the entire chain of Promises.

 const one = (json) => new Promise((resolve, reject) => { resolve(JSON.parse(json)) }) const two = (json) => one(json).then(data => data.hello) const three = (json) => two(json).then(hello => hello.toUpperCase()) three('{"hello":"world"}').then(console.log, console.error) // "WORLD" three('bad json').then(console.log, console.error) // Error: unexpected token b in JSON at position 0 


Otherwise by "asynchronous function" you mean it's a function that does not return a Promise and maybe uses a continuation instead? In which case, we'll modify one to wrap the async function in a promise, then two and three will work the same. Of importance, I did not use try / catch in any of my Promise functions

 // continuation passing style async function const asyncParse = (json, k) => { try { k(null, JSON.parse(json)) } catch (err) { k(err) } } // one now wraps asyncParse in a promise const one = (json) => new Promise((resolve, reject) => { asyncParse(json, (err, data) => { if (err) reject(err) else resolve(data) }) }) // everything below stays the same const two = (json) => one(json).then(data => data.hello) const three = (json) => two(json).then(hello => hello.toUpperCase()) three('{"hello":"world"}').then(console.log, console.error) // "WORLD" three('bad json').then(console.log, console.error) // Error: unexpected token b in JSON at position 0 


Oh, and if you have an a function f which does not function in either of these two ways – ie function that throws an error but doesn't return a promise or send the error to the continuation - you're dealing with a piece of rubbish and the code you write to depend on f will be rubbish too.

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