Javascript async catch errors at caller - javascript

If I add the keyword async to a function, it seems that I have to catch errors "in" that function. Sometimes it doesn't make sense to catch errors and I want to defer them to the caller as I may not know the context in which the function is called (e.g. is the caller doing res.json(e) or next(e), or neither)
Is there a way around this? So I can use async (in order to await inside the function) and defer errors to the caller?
Here is a really contrived example
https://codepen.io/anon/pen/OzEXwM?editors=1012
try {
function example(obj = {}) {
try {
obj.test = async () => { // if I remove async keyword, it works, otherwise I'm forced to catch errors here
//try{
throw new Error('example error') // I know `example outer error` won't catch as the returned object loses the context
//}catch(e){
// console.log('I do not want to catch error here'), I wan't to defer it to the caller
//}
}
return obj
} catch (e) {
console.log('example outer error')
}
}
let doit = example()
doit.test() // why doesn't 'outer error' catch this?
} catch (e) {
console.log('outer error')
}
The script ran as is will give an Uncaught Exception. But if I remove the async keyword it works (yes, I know in this example, async is silly, it's just an example)
Why can't I catch the error when doit.test() is called?
Usually, I would just comply and rework, but in trying to explain to someone else this morning, I realized, I didn't really know the answer.

Why can't I catch the error when doit.test() is called?
Because it is async. By the time it has reached the throw error part the try catch block on the outside has already been executed and passed. There is nothing to throw to so to speak.
To fix this, since async and await are just syntactic sugar for Promises you just use it's catch callback. Your test() function is going to return a promise so just add a the callback onto the returned promise
doit.test().catch(()=>{
console.log('Any uncaught exceptions will be sent to here now');
});
Demo
function example(obj = {}) {
obj.test = async() => {
throw new Error('example error')
}
return obj;
}
let doit = example()
doit.test().catch(e => {
console.log('Caught an exception: ', e.message);
});

If you want to catch error of test() function. You need to do await doit.test()
https://jsfiddle.net/1tgqvwof/
I wrapped your code in a anonymous function since await must be within async function
(async function () {
try {
async function example(obj = {}) {
try {
obj.test = async () => { // if I remove async keyword, it works, otherwise I'm forced to catch errors here
//try{
throw new Error('example error') // I know `example outer error` won't catch as the returned object loses the context
//}catch(e){
// console.log('I do not want to catch error here'), I wan't to defer it to the caller
//}
}
return obj
} catch (e) {
console.log('example outer error')
}
}
let doit = example()
await doit.test() // why doesn't 'outer error' catch this?
} catch (e) {
console.log('outer error')
}
})();

Related

How to catch errors from a chain of async functions of a third party library?

I need to catch an error from a third party library.
(async () => {
try {
var response = await func_1();
console.log("should not show this");
}
catch (err) {
console.log("should show this", err);
}
async function func_1() {
func_2();
}
async function func_2() {
func_N();
}
async function func_N() {
throw new Error("oops");
}
})();
So I need to catch func_N error in try catch but becuase func_N is async and it's called by some functions without await the try catch doesn't catch the error.
So is there any way to do this?
func_N is async and it's called by some functions without await
(and without return)
Then they need to fix that. There's nothing you can do about this otherwise.

Unable to catch error thrown by Async function

I was reading this article. In the below code the async 'foo' function returns the result of the async 'waitAndMaybeReject' function.
I am unable to understand why the catch block in 'foo' function is not executed when 'waitAndMaybeReject' throws an error.
async function waitAndMaybeReject() {
await new Promise(r => setTimeout(r, 5000));
const isHeads = false;
//const isHeads = true;
if (isHeads) return 'yay';
throw Error('Boo!');
}
async function foo() {
try {
return waitAndMaybeReject();
}
catch (e) {
return 'caught';
}
}
foo();
You need to await the waitAndMaybeReject() function in order to be able to catch the exception. As it is described in the article you refer to:
By returning waitAndMaybeReject(), we're deferring to its result, so our catch block never runs.
To make it simple, when you return waitAndMaybeReject() you don't wait for it to resolve/reject in the try ... catch block.

Javascript - Throw different error from inner function and general in the outer

I have the following code for my top/outer method "f":
async function f() {
await g().catch(err => {...});
...
}
And this one for the method which is executed inside f():
async function g() {
await something(); // Can fail because of server problems...
if (true) {
throw new functions.https.HttpsError("cancelled", "Some custom message");
}
}
Now, if in f, I want to throw the errors as instances of functions.https.HttpsError, I have thought to do:
async function f() {
await g().catch(err => {
console.error(err);
throw new functions.https.HttpsError("unavailable", "Server error") // Unexpected errors... Will be good for errors coming from calling something() in g()
/* Note: HttpsError -> constructor(type, message) */
});
...
}
As you can see, I am handling the general errors, with the type "unavailable" and a general message "Server error".
But... what happens with the error coming from the conditional? How can I catch it in f(), and throw it? Its type is "cancelled" and the message is "Some custom message", but this way I am handling errors will result in a problem, as the "custom" error is an instance of the same class of error I am throwing in the outer function when catching errors from the inner.
I have thought to throw an object
async function g() {
...
if (true) {
const error = {
type: "cancelled",
message: "custom error",
}
throw error;
}
}
And then, in f() do:
await g().catch(err => {
console.error(err);
throw new functions.https.HttpsError(err.type || "unavailable", err.message || "Server error") // Unexpected errors... Will be good for errors coming from calling something() in g()
/* Note: HttpsError -> constructor(type, message) */
});
But the code doesn't look clean. Is this the common way to handle these situations? Any other better
Thank you. way?
If you want to convert the error you get from g() to something else, the idiomatic way to do that is with a catch block:
async function f() {
try {
await g();
// ...perhaps other things here as well...
} catch (err) {
console.error(err);
throw new functions.https.HttpsError("unavailable", "Server error");
}
// ...
}
Remember that in an async function, await promise will raise an error if the promise is rejected. You catch that error using catch, just like a synchronous error.

Error thrown inside generator finishes it despite being caught

I have an async generator function, which inside calls few async functions which can throw errors. What I want is that when error occurs, generator just logs it but then continue to work further. So i have a code like this...
async * getAll (somestuff) {
try {
const thing = await fetchThing()
const otherThing = await fetchAnother()
yield {
...thing,
...otherThing
}
} catch (error) {
console.log('Error happened, but thats ok, I want to continue')
}
}
But when error occurs, it gets logged by the catch block, but then generator yields { done: true } and operation stops.
I have tried manually yielding null after console.log in catch block but with the same result.
This kind of "problem" is not related to the generator itself, it's just related to the await mechanism inside a try..catch block, because whenever a promise is rejected inside a try-catch block, the catch is joined (unless the promises is try-catched separetely).
In fact, the generator cannot go any further, because once the catch is somehow reached, it will continue until another yield is invoked. If none needs to be invoked, it just finishes giving done: true, and that's the intended behavior of a generator.
Your main issue, is that you are expecting a generator to yield all the values, but it just can't, because the yield block is never reached:
try {
const thing = await fetchThing()
const otherThing = await fetchAnother()
yield { // <-- never met if either thing or otherThing are rejected.
...thing,
...otherThing
}
} catch (error) { // <-- this block is reached whenever either thing or otherThing raise an exception.
console.log('Error happened, but thats ok, I want to continue')
}
If you want your try..catch block to continue if either of the inner awaitable elements raised an exception, you need to try-catch them as well, so that you can have further control over their "failing" behavior:
try {
let thing, otherThing;
try {
thing = await fetchThing()
otherThing = await fetchAnother()
}
catch (innerException) {
console.log('either of the above failed!', innerException);
}
// in this way, the below block will be reached.
yield {
...thing,
...otherThing
}
} catch (error) {
console.log('Error happened, but thats ok, I want to continue')
}
In this way, whether both fails or both will be successful the yield block will be reached and will continue the execution.
Here is an example showing what said above:
const fakeAsync = async () => await Promise.resolve(true);
const fakeAsyncReject = async () => await Promise.reject(false);
async function* getAll(someStuff) {
try {
let res, raiseExc;
try {
res = await fakeAsync();
}
catch (innerResException) {
console.log('an inner exception raised.');
}
try {
raiseExc = await fakeAsyncReject();
}
catch (innerRaiseException) {
console.log('an inner exception was raised.', innerRaiseException);
}
// yield block, always reached unless there is something really wrong in this try block, like syntax errors or whatever.
yield {
res,
raiseExc
}
}
catch (error) {
// catch block raised only when the try block above yields an exception, NOT raised when either of the try-catch blocks inside the try-catch actually join the catch.
console.log('Error happened', error);
}
}
// Uncomment this block if you want to see how the for-await would work.
/*
(async() => {
for await (var res of getAll([])) {
console.log('res is', res);
}
})();
*/
(async() => {
const asyncIterator = getAll([]);
console.log(await asyncIterator.next());
})();

How to ensure an error from an async function is captured without rethrowing the error or using Promise.reject?

Please, look at the following code. Line 5: return ex.
When I call myFunc, I expect to see 'err' in the console, but I see 'yes' which means that outside of myFunc the error is not being captured. The error is not bubbling up.
Which makes sense because I'm not rethrowing the error doing: throw ex or using Promise.reject(ex).
My Question is: How to ensure the error is captured outside of my function without using the two methods mentioned above? Is there a way?
async function myFunc() {
try {
throw new Error();
} catch (ex) {
return ex;
}
}
myFunc().then(() => console.log('yes')).catch(() => console.log('err'))
// 'yes'
When using async functions, you can use the normal javascript try / catch to handle errors.
If you throw within an async function, the promise it returned will be rejected.
If you return from an async function (like you did in your catch block), the promise will be resolved with the returned value (in your case the exception)
Here two examples how you can handle an async function that might throw:
// async function that might throw
async function funcThatThrows() {
throw new Error("IT THROWS!");
}
// Exception Handling in other async functions
async function example1() {
try {
let result = await funcThatThrows();
/* ... */
} catch(e) {
console.log("example1:", e.message);
}
}
// Exception handling in non-async functions
function example2() {
funcThatThrows()
.then(result => { /* ... */ })
.catch(err => console.log("example2:", err.message));
}
example1();
example2();
See Javascript Async/Await Error Handling for a detailed tutorial on async error handling :)
The best way is to do this:
async function myFunc() {
try {
throw new Error();
} catch (ex) {
throw ex;
}
}
I had an attempt at answering the question but ended up rewriting my whole answer. At one point, I deleted my answer.
As you already stated in the question, throw ex or Promise.reject(ex) are the only ways.
When I use async function in my application, I usually do so as a means to migrate away from Promise chaining and dropping then and catch. It's really odd that you mix async function but call it with Promise, then and catch syntax.
Actually, I think it's dangerous to mix it as indicated here: JavaScript Promises - reject vs. throw
If you indulge me, I want to rewrite your caller using async function and await:
async function myFunc() {
throw new Error("Some Error!");
}
( async function () {
try {
await myFunc();
console.log("yes");
} catch (err) {
console.log("err: ", err.message);
}
} )();
This syntax is similar to its synchronous counterpart which infers we ought to use throw:
function myFunc() {
throw new Error("Some Error!");
}
( function () {
try {
myFunc();
console.log("yes");
} catch (err) {
console.log("err: ", err.message);
}
} )();

Categories

Resources