How to handle async errors correctly? - javascript

When making a GraphQL query, and the query fails, Apollo solves this by having a data-object and an error-object.
When an async error is happening, we get the same functionality with one data-object and one error-object. But, this time we get an UnhandledPromiseRejectionWarning too, with information about: DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code..
So, we obviously need to solve this, but we want our async-functions to cast errors all the way up to Apollo. Do we need to try...catch all functions and just pass our error further up the tree? Coming from C#, were an exception just goes all the way to the top if never caught, it sounds like a tedious job to tell Apollo GraphQL that one (or more) leaves failed to retrieve data from the database.
Is there a better way to solve this, or is there any way to tell javascript/node that an uncaught error should be passed further up the call tree, until it's caught?

If you correctly chain your promises, you should never see this warning and all of your errors will be caught by GraphQL. Assume we have these two functions that return a Promise, the latter of which always rejects:
async function doSomething() {
return
}
async function alwaysReject() {
return Promise.reject(new Error('Oh no!'))
}
First, some correct examples:
someField: async () => {
await alwaysReject()
await doSomething()
},
// Or without async/await syntax
someField: () => {
return alwaysReject()
.then(() => {
return doSomething()
})
// or...
return alwaysReject().then(doSomething)
},
In all of these cases, you'll see the error inside the errors array and no warning in your console. We could reverse the order of the functions (calling doSomething first) and this would still be the case.
Now, let's break our code:
someField: async () => {
alwaysReject()
await doSomething()
},
someField: () => {
alwaysReject() // <-- Note the missing return
.then(() => {
return doSomething()
})
},
In these examples, we're firing off the function, but we're not awaiting the returned Promise. That means execution of our resolver continues. If the unawaited Promise resolves, there's nothing we can do with its result -- if it rejects, there's nothing we can do about the error (it's unhandled, as the warning indicates).
In general, you should always ensure your Promises are chained correctly as shown above. This is significantly easier to do with async/await syntax, since it's exceptionally easy to miss a return without it.
What about side effects?
There may be functions that return a Promise that you want to run, but don't want to pause your resolver's execution for. Whether the Promise resolves or returns is irrelevant to what your resolver returns, you just need it to run. In these cases, we just need a catch to handle the promise being rejected:
someField: async () => {
alwaysReject()
.catch((error) => {
// Do something with the error
})
await doSomething()
},
Here, we call alwaysReject and execution continues onto doSomething. If alwaysReject eventually rejects, the error will be caught and no warning will be shown in the console.
Note: These "side effects" are not awaited, meaning GraphQL execution will continue and could very well finish while they are still running. There's no way to include errors from side effects inside your GraphQL response (i.e. the errors array), at best you can just log them. If you want a particular Promise's rejection reason to show up in the response, you need to await it inside your resolver instead of treating it like a side effect.
A final word on try/catch and catch
When dealing with Promises, we often see errors caught after our function call, for example:
try {
await doSomething()
} catch (error) {
// handle error
}
return doSomething.catch((error) => {
//handle error
})
This is important inside a synchronous context (for example, when building a REST api with express). Failing to catch rejected promises will result in the familiar UnhandledPromiseRejectionWarning. However, because GraphQL's execution layer effectively functions as one giant try/catch, it's not really necessary to catch your errors as long as your Promises are chained/awaited properly. This is true unless A) you're dealing with side effects as already illustrated, or B) you want to prevent the error from bubbling up:
try {
// execution halts because we await
await alwaysReject()
catch (error) {
// error is caught, so execution will continue (unless I throw the error)
// because the resolver itself doesn't reject, the error won't be bubbled up
}
await doSomething()

Related

Error not catching in the try/catch when remove the await [duplicate]

const errorTest = async() => {
const result = await $.get("http://dataa.fixer.io/api/latest?access_key=9790286e305d82fbde77cc1948cf847c&format=1");
return result;
}
try {
errorTest()
}
catch(err) {
console.log("OUTSIDE ERROR!" + err)
}
The URL is intentionally incorrect to throw an error, but the outside catch() it not capturing it. Why?
If I use then() and catch() instead, it works.
errorTest()
.then(val=> console.log(val))
.catch(err=> console.error("ERROR OCCURRED"))
This works, but the try {..} catch() doesn't. Why?
I keep getting the Uncaught (in promise) error.
async function errorTest() { /* ... */ }
try {
errorTest()
}
catch(err) {
console.log("OUTSIDE ERROR!" + err)
}
Because errorTest is async, it will always return a promise and it is never guaranteed to finish execution before the next statement begins: it is asynchronous. errorTest returns, and you exit the try block, very likely before errorTest is fully run. Therefore, your catch block will never fire, because nothing in errorTest would synchronously throw an exception.
Promise rejection and exceptions are two different channels of failure: promise rejection is asynchronous, and exceptions are synchronous. async will kindly convert synchronous exceptions (throw) to asynchronous exceptions (promise rejection), but otherwise these are two entirely different systems.
(I'd previously written that async functions do not begin to run immediately, which was my mistake: As on MDN, async functions do start to run immediately but pause at the first await point, but their thrown errors are converted to promise rejections even if they do happen immediately.)
function errorTest() {
return new Promise(/* ... */); // nothing throws!
}
function errorTestSynchronous() {
throw new Error(/* ... */); // always throws synchronously
}
function errorTestMixed() {
// throws synchronously 50% of the time, rejects 50% of the time,
// and annoys developers 100% of the time
if (Math.random() < 0.5) throw new Error();
return new Promise((resolve, reject) => { reject(); });
}
Here you can see various forms of throwing. The first, errorTest, is exactly equivalent to yours: an async function works as though you've refactored your code into a new Promise. The second, errorTestSynchronous, throws synchronously: it would trigger your catch block, but because it's synchronous, you've lost your chance to react to other asynchronous actions like your $.get call. Finally, errorTestMixed can fail both ways: It can throw, or it can reject the promise.
Since all synchronous errors can be made asynchronous, and all asynchronous code should have .catch() promise chaining for errors anyway, it's rare to need both types of error in the same function and it is usually better style to always use asynchronous errors for async or Promise-returning functions—even if those come via a throw statement in an async function.
As in Ayotunde Ajayi's answer, you can solve this by using await to convert your asynchronous error to appear synchronously, since await will unwrap a Promise failure back into a thrown exception:
// within an async function
try {
await errorTest()
}
catch(err) {
console.log("OUTSIDE ERROR!" + err)
}
But behind the scenes, it will appear exactly as you suggested in your question:
errorTest()
.then(val=> console.log(val))
.catch(err=> console.error("ERROR OCCURRED"))
You need to await errorTest
const callFunction=async()=>{
try{
const result = await errorTest()
}catch(err){
console.log(err)
}
}
callFunction ()
Note that the await errorTest() function has to also be in an async function. That's why I put it inside callFunction ()
Another Option
const errorTest = async() => {
try{
const result = await $.get("http://dataa.fixer.io/api/latest?access_key=9790286e305d82fbde77cc1948cf847c&format=1");
console.log(result)
}catch(err){
console.log(err)
}
}
I think the fundamental misunderstanding here is how the event loop works. Because javascript is single threaded and non-blocking, any asynchronous code is taken out of the normal flow of execution. So your code will call errorTest, and because the call to $.get performs a blocking operation (trying to make a network request) the runtime will skip errorTest (unless you await it, as the other answers have mentioned) and continue executing.
That means the runtime will immediately jump back up to your try/catch, consider no exceptions to have been thrown, and then continue executing statements which come after your try/catch (if any).
Once all your user code has ran and the call stack is empty, the event loop will check if there are any callbacks that need to be ran in the event queue (see diagram below). Chaining .then on your async code is equivalent to defining a callback. If the blocking operation to $.get completed successfully, it would have put your callback in the event queue with the result of errorTest() to be executed.
If, however, it didn't run successfully (it threw an exception), that exception would bubble up, as all exceptions do until they're caught. If you have defined a .catch, that would be a callback to handle the exception and that'll get placed on the event queue to run. If you did not, the exception bubbles up to the event loop itself and results in the error you saw (Uncaught (in promise) error) -- because the exception was never caught.
Remember, your try/catch has long since finished executing and that function doesn't exist anymore as far as the runtime is concerned, so it can't help you handle that exception.
Now if you add an await before errorTest() the runtime doesn't execute any of your other code until $.get completes. In that case your function is still around to catch the exception, which is why it works. But you can only call await in functions themselves that are prefixed with async, which is what the other commenters are indicating.
Diagram is from https://www.educative.io/answers/what-is-an-event-loop-in-javascript. Recommend you check it out as well as https://www.digitalocean.com/community/tutorials/understanding-the-event-loop-callbacks-promises-and-async-await-in-javascript to improve your understanding of these concepts.

How to handle error and use promises correctly

To begin with, I am taking a follow up from this question I posted few moments ago
Now, I thought I knew Aysnc and Promise but clearly I am missing something.
Referencing to the Tagged answer by estus,
Unless API supports promises, errors should be entirely handled in
async function. Function body should be wrapped with try..catch to
rule out unhandled rejections which may result in exceptions in future
Node versions
From which I was able to comprehend that whenever we are using aysnc function and we want to do error Handling we need to use try..catch and for normal Promises we could simple do resolve/reject or if it is already a promise then we can chain and do .then and .catch but for that I got following reply on comment
Yes, you can expect an error. async/await is just syntactic sugar for
raw promises. Any async function can be rewritten in plain ES6
I might be keeping this question broad but can someone please help me in explaining..
When do we need to use
.then and .catch
and when do we need to use
try..catch
Also, What does it mean by
Unless API supports promises
Async await code looks cleaner and easy to read. The Promises were created to solve the callback hell problem but chaining a lot of promises also confusing. So async wait is a syntactical sugar and you can use any one of the .then or async await.
If you are using simple promises syntax then you can use .then.then.then.catch() syntax.
if you are using async and await then you have to use try catch. All the await will go in try and the catch condition would go in single catch.
Both these can be used when API/function you are using returns a promise.
try...catch in async function is syntactic sugar for catch() in raw promise. If raw promises are used for some reason (legacy code) then catch() may be used. This shouldn't be a problem in Node, since recent versions support async..await.
Notice that try..catch catches both synchronous and asynchronous errors in async. This should be taken into account to not leave synchronous errors unhandled with plain promise.
If API doesn't support promises, you cannot expect that promise rejection that is returned from a function be handled by API, so you need to do this yourself.
// So this is how promises work (as you may already be familiar with)
function promiseFu() {
return new Promise((resolve, reject) => {
reject();
})
.catch(e => {
// The exception has been handled
console.error("Error begin in promiseFu()!")
throw e; // <- now this triggers the second exception
})
}
// Asynchronous functions are the functions that ALWAYS returns a promise
// therefore, this is also correct
async function asyncFu() {
return new Promise((resolve, reject) => {
reject();
})
.catch(e => {
// The exception has been handled
console.error("Error begin in promiseFu()!")
throw e; // <- now this triggers the second exception
})
.catch(e => {
// Here the second exception gets handled as well, and asyncFu does not throw any exception in the end
})
}
// Now the power of async await
async function asyncMainFu() {
// using await before an async function would make it wait for the function to complete asynchronously before moving on
await asyncFu()
// await would do the same with promises as well
// await promiseFu() // <- this is correct as well
// BUT now, if you see promiseFu() is throwing the second exception which is not yet handled,
// asyncMainFu() would throw the same exception as well. unless handled by a try..catch block
try {
await promiseFu()
} catch(e) {
// handling the exception thrown by promiseFu and not throwing any new exception
// is a good idea if you think caller of asyncMainFu() might not handle it.
}
}

JavaScript Promise: Reject handler vs catch [duplicate]

This question already has answers here:
When is .then(success, fail) considered an antipattern for promises?
(7 answers)
Closed 4 years ago.
I have come across multiple applications where using catch is preferred over rejectHandler.
Eg:
Preferring
new Promise.then(resolveHandler).catch()
instead of
new Promise().then(resolveHandler, rejectHandler).catch()
Is there a particular reason for this??
I find
new Promise().then(resolveHandler, rejectHandler).catch()
to be more useful because
I can use rejectHandler to address designed/expected error scenario where Promise.reject is called.
I can use catch block to address unknown/unexpected programming/runtime errors that occur.
Does someone know any particular reason why rejectHandler is not used much?
P.S. I am aware of newer alternatives in ES6 but I just curious to know this.
Update: I KNOW HOW rejectHandler and catch works. The question is why do I see more people use only catch over both rejectHandler and catch? Is this a best practice or there is some advantage?
Update(Adding answer here): Found the answer I was looking for first hand.
The reason is not just because the error in reject is handled by catch it is mainly because of chaining. When we are chaining promise.then.then.then.then, having a resolve, reject pattern proves a bit tricky to chain it since you wouldn't want to implement a rejecthandler just to forward the rejectData up the chain. Using only promise/then/catch along with resolve/return/throw proves very useful in chaining N numbers of thenables.
#Bob-Fanger(accepted answer) addressed some part of this too.
Eg:
getData(id) {
return service.getData().then(dataList => {
const data = dataList.find(data => {
return data.id === id;
});
if (!data) {
// If I use Promise.reject here and use a reject handler in the parent then the parent might just be using the handler to route the error upwards in the chain
//If I use Promise.reject here and parent doesn't use reject handler then it goes to catch which can be just achieved using throw.
throw {
code: 404,
message: 'Data not present for this ID'
};
}
return configuration;
});
}
//somewhere up the chain
....getConfiguration()
.then(() => {
//successful promise execution
})
.catch(err => {
if (err.code) {
// checked exception
send(err);
} else {
//unchecked exception
send({
code: 500,
message: `Internal Server error: ${err}`
});
}
});
Using just these All I need to worry about is promise/then/catch along with resolve/return/throw anywhere in the chain.
The difference is that if an error occurs inside resolveHandler it won't be handled by the rejectHandler, that one only handles rejections in the original promise.
The rejectHandler is not used in combination with catch that much, because most of the time we only care about that something went wrong.
Creating only one errorhandler makes the code easier to reason about.
If a specific promise in the chain should handled differently that can be a reason to use a rejectHandler, but i'd probably write a catch().then().catch() in that case.
Neither is more useful than the other. Both the rejected handler and the catch callback are called when an error is thrown or a promise is rejected.
There is no "best practice" to use one over the other. You may see code use one or the other, but it's use will be based on what the code needs to achieve. The programmer may want to catch an error at different times in the chain and handle errors thrown at different times differently.
Hopefully the following will help explain what I mean:
somePromise
.then(
function() { /* code when somePromise has resolved */ },
function() {
/* code when somePromise has thrown or has been rejected.
An error thrown in the resolvedHandler
will NOT be handled by this callback */ }
);
somePromise
.then(
function() { /* code when somePromise has resolved */ }
)
.catch(
function() {
/* code when somePromise has thrown or has been rejected OR
when whatever has occurred in the .then
chained to somePromise has thrown or
the promise returned from it has been rejected */ }
);
Notice that in the first snippet, if the resolved handler throws then there is no rejected handler (or catch callback) that can catch the error. An error thrown in a resolved callback will not be caught by the rejectedHandler that is specified as the second argument to the .then
As stated in the post, provision of a resolve and reject handler in the same call to .then allows dealing with rejection of the previous promise separately from errors thrown within, or returning a rejected promise from, the success handler.
Because a rejection handler returning without throwing an error resumes the fufilled channel of a promise chain, a final catch handler will not be invoked if a previous rejection handler returns normally.
The question then devolves into use cases, cost of development and level of knowledge.
Use cases
In theory the two parameter form of then call could be used to retry an operation. But because hard coded promise chains are set up statically, retrying the operation is not simple. An easier way to retry might be to use an async function with try-catch statements surrounding await of a promise that may need to be retried as in this concept code:
async function() {
let retries = 3;
let failureErr = null;
while( retries--) {
try {
var result = await retryableOperationPromise()
return result;
}
catch( err) {
failureErr = err;
}
}
throw failureErr // no more retries
}
Other use cases may not be widespread.
Cost of development or commercial decisions.
If telling a user to retry later is acceptable it may be cheaper than doing anything about specific reasons for promise rejection. For example if I try to book a flight over midnight, when airlines put the prices up, I usually get told "an error occurred, try again later" because the price I was given at the start of booking will not be honored.
Knowledge (<opinion>)
I suspect that promise usage may often be based on example rather than in-depth knowledge of the subject. It is also possible program managers want to keep the code base as simple as possible for less experienced developers (probably a cost issue).
"Best practice" may not truly apply for making decisions on how to use promises if the usage is valid. Some developers and managers will avoid some forms of usage as a matter of principle, but not always based on technical merit.

Is there any way to check error in each Promise in promise.all when there is a promise error

I have one array of promises that each one execute a code that can have a javascript error and break the script. I need to check for each promise and catch any error. The problem is that the promise has timeout functions inside. Is there anyway to solve this?
Example code:
function apiRequest(url,customError) {
return new Promise(function (resolve, reject) {
if(customError) {
setTimeout(() => {
//Force error
var ar = url.asdasd.eadasd;
ar = ar.split('123')
},3000)
}
if (url) {
return resolve(url);
} else {
return reject('apiRequest failed!');
}
})
.catch(function(err){
return 'error on javascript code';
});
}
var p1 = apiRequest('urlOne',true);
var p2 = apiRequest(false,false);
var p3 = apiRequest('urlThree');
Promise.all([p1, p2, p3])
.then(function(res){
console.log('Promise.all', res);
}, error => {
console.log('Error on reject')
})
.catch(function(err){
console.error('err', err);
});
Result:
Promise.all [ 'urlOne', 'error on javascript code', 'urlThree' ]
var ar = url.asdasd.eadasd;
TypeError: Cannot read property 'eadasd' of undefined
If there is an error inside each promise, my code can catch it but if there is a timeout and the error happends after the promise finish I cant catch it and my code breaks, is there anyway to catch this error?
Is there any way to check error in each Promise in promise.all when there is a promise error?
By design, Promise.all() resolves when ALL promises you passed it have resolved or it rejects when any single promise in it rejects. It does not, by design, wait for all promises to resolve or reject and then give you all the results whether they resolved or rejected.
Functionality like that is typically named something like Promise.settle() and you can fairly easily build that type of functionality by just adding a .catch() handler to each promise you pass Promise.all() so that instead of rejecting, it resolves, but with a value that you can later tell that it actually rejected.
You can see several various implementations of .settle() type functionality in this answer:
ES6 Promise.all() error handle - Is .settle() needed?
If there is an error inside each promise, my code can catch it but if there is a timeout and the error happends after the promise finish I cant catch it and my code breaks, is there anyway to catch this error?
The way you have structured your setTimeout(), it is not connected at all to the promise that it is inside of. If you want them connected, then you have to wait to resolve until after the timeout is done and then, and only then, can you know if you should resolve or reject.
Since the code you show inside your setTimeout() looks like pseudo-code (that doesn't actually do anything yet), it's hard for us to see exactly what the point of the setTimeout() is to know exactly what you are trying to achieve and thus what a good suggestion would be.
This answer about using setTimeout() as part of a promise chain might be relevant:
using setTimeout on promise chain.
In that case, the timer is inserted into the promise chain so that things are sequenced before it and after it. As you show it now, it's a completely separate parallel path of execution that has no connection at all to your promise chain.
If all you're trying to do with the setTimeout() is to invoke a timeout if your api request does not return before the timer fires, then you can likely implement that by just calling reject() inside the setTimeout(). If the api request has already completed and already called resolve(), then calling reject() will do nothing at that point. If the api request has not yet finished, then calling reject() will reject the host promise.

Promise.catch is swallowing errors

I've done a lot of async coding in Node.js with callbacks and the excellent async library which works great. I'm trying to use a module that uses promises but I'm running into a problem where any errors thrown AFTER the promise are still bubbled up and caught by the promise error handler.
This makes it very difficult to debug errors as I have no idea where they will pop up and they can't be thrown and don't crash the app.
Example code below; all I want to do is to exit the promise chain and leave it behind once it has been resolved, rather than it catching all subsequent errors that aren't anything to do with it.
function one (input, callback) {
doSomeAsyncWork(input)
.then(function (result) {
return callback(null, result);
})
.catch(function (err) {
logError(err);
return callback(err);
});
}
function two (err, result) {
if (err) { ... }
var x = callAMethodThatThrows();
...
}
one('abc', two);
In this example, the method callAMethodThatThrows() throws an error, which gets bubbled up to the promise catch() block. This prevents the app from crashing and leaves it in an unknown state.
Any advice would be really appreciated, thanks.
Yes, sorry about that - we're getting to fixing(1) the default behavior in Node. In the meanwhile I specced and Petka added (with support from others) a hook for finding these errors:
process.on("unhandledRejection", (err, p) => {
console.error(err); // print the error
});
Note that this might catch some false negatives if you're performing .catch asynchronously itself - in my experience that's very rare.
Also note that with promises your server typically isn't in an unknown state and you can and should attempt to recover from errors when it makes sense. Since promises all the way means throw-safe code you can have fine grained error handling.
Note that promises make the async library largely unneeded. If you still wish to use callbacks and just wish "those pesky promises" would leave you alone and let you keep writing callbacks, that's fine - promises are throw safe but you can escape that by executing things off the promise code:
myPromiseFn().then(v => {
process.nextTick(() => cb(null, v)); // next tick, to escape the chain
}, e => process.nextTick(() => cb(e));
Note that fair promise libraries also come with a asCallback callback for converting the promise code to a node err-back callback.
(1) Some people claim there is no problem, go figure
Thanks to Ben's answer I discovered it's possible to convert a promise to a callback using a module such as nodeify. This allows us to keep all our code using callbacks, and avoids errors getting swallowed by the promise. Very useful.

Categories