How to handle error and use promises correctly - javascript

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.
}
}

Related

How to handle async errors correctly?

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()

Execution of then() function in JavaScript

I am new to asynchronous JavaScript, while learning Promise in JavaScript, I wrote a simple program
var p=new Promise(function(resolve,reject)
{
//Any async task.
var IsPromiseFulfilled=true;
if(IsPromiseFulfilled){
resolve("Promise Fulfilled");
}
else
{
reject("Promise Rejected");
}
});
p.then(function(status){
console.log(status);
}).catch(function(status){
console.log(status);
});
console.log("End Of Program");
The Output was:
End Of Program
Promise Fulfilled
Can anyone please tell me why "End Of Program" was printed earlier than "Promise Fulfilled"
Because it's executed outside of your promises and in turn executes before the promises resolve.
Although the Promise.resolve().then() call isn't slow, it will process all the code then start resolving the promises.
It's also worth noting you can shorten things a little as well by using promises like:
Promise.resolve().then(result => {
// do something with your result
}).catch(error => {
// do something with your error.
});
I find this format easier than using new Promise(function (resolve, reject) {})
It works this way because of the way .then() handlers are described in the promise specification.
All .then() handlers are executed on a future tick of the event loop. That means that any synchronous code executes before any .then() handler even if the promise is resolved immediately.
Here's a very simple example:
console.log("a");
Promise.resolve().then(() => {console.log("b");});
console.log("c");
This will output:
a
c
b
This is per the promise specification and it is done this way to guarantee that .then() handler are ALWAYS asynchronous and thus have a consistent and predictable behavior, regardless of whether the promise is resolved immediately or some time in the future. It makes writing and testing code a lot simpler.
From the Promises A+ specification:
2.2.4 onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1].
"platform code" here means that the current execution of Javascript finishes and then when the stack is completely empty of any Javascript, then and only then are promises .then() handlers called.
So, in my example above, the promise is resolved immediately, and the .then() handler is scheduled immediately, but it is scheduled to run AFTER the rest of the synchronous code finishes (essentially on the next turn of the event loop).
In the ES6 specification, when a promise resolves, its .then() handlers are scheduled with enqueJob(). The concept of jobs in ES6 is described here. What's relevant here is that they run AFTER the currently executing Javascript is done.
p.then happens in the future. While node waits for the then promise to resolve it will execute the next line of code.
Which is the console.log("End Of Program");
If you want to execute your Promises and async functions in a linear way, you must use the
ES2017 async/await syntax
Any Promise inside a async function can use the syntax await, for that you don't have to use .then() or .catch()
(async function () {
try {
var status = await p()
console.log(status)
console.log("End Of Program")
} catch (e) {
console.log(e) //If p reject, you catch it here
}
})()
And you should write your Promise like this
var p = new Promise(function(resolve,reject) {
/* When you use asynchronous functions, they all have a callback
This callback may have some error and data params, so,
you use them to resolve or reject your promise
*/
someAsyncFunction('blabla', function(error, data) {
if (error)
reject(error)
else
resolve(data)
})
})
I don't pretend for academic answer but I already asked amost the same question
today, about promises.
Actuall answer is:
When you ask to do work in JS, what is says that, "OK, this is going to take some time, but I promise that I'll let you know after the work is done. So please have faith on my promise, I'll let you know once the work is complete", and it immediately gives you a promise to you.
That's why you see your:
End Of Program
Promise Fullfilled
The actuall scheme looks like this:
│
├┐
│↓ promise
↓
console.log

Code works, but is it best practice to have a try/catch block inside a promise?

Is it good practice to wrap a try/catch with a Promise? I was reviewing the code below, I cannot seem to understand the necessity of having a try/catch block inside a Promise. Also, why would there be no place where the Promise rejects, as you can see in the catch block, the Promise resolves. Please help me understand this piece of code, in terms of best practice and efficiency the point of having the try/catch inside the Promise.
I would also really appreciate any suggestions regarding cleaning the code up where it is redundant.
getRoute(): any {
return async (request: any, reply: any) => {
const result = new Promise<string>( async (resolve, reject) => {
try {
let userIsValid = await this.validator.validate(request.payload, RegisterUserValidator);
if (userIsValid.error) {
resolve(responseHelper.getErrorResponse(ResponseErrorCode.joiValidatorError, userIsValid.error));
throw new ControlFlowNonError();
}
let results = await this.authUser.registerUser(request.payload);
resolve(responseHelper.getSuccessResponse(results, null));
}
catch (error) {
let message: ILogMessage = {
code: ResponseErrorCode.unknownError,
message: ResponseErrorCode.unknownError.toString(),
meta: error,
sourceFunction : 'ApiDataCreateCredentials: getRoute()'
};
this.logHelper.error(message);
resolve(error);
}
});
reply(result);
};
};
Is it best practice to have a try/catch block inside a promise [executor function]?
No, it's not best practice. There's pretty much no reason to use promises inside a promise executor function. Because when you're doing that, you don't need the outer, manually created promise at all. You can just return the inner promise. That's the Promise constructor anti-pattern.
FYI, though it isn't your case here, it is reasonable to use try/catch to handle regular exceptions inside a promise executor (if the manually created promise executor is actually needed in the first place and if regular exceptions are of concern and you want to handle them locally).
Here's another idea for how to accomplish your same logic. This removes the promise anti-pattern of surrounding a promise with another manually created one and uses promise flow control to make userIsValid.error go to the log error code. I didn't see any particular advantage of using await here so I switched back to just using .then() and .catch(). I don't know TypeScript so this is a regular Javascript version of the code, but you can presumably add the slight syntax differences yourself to turn it back to TypeScript:
getRoute(): function() {
return function(request, reply) {
return this.validator.validate(request.payload, RegisterUserValidator).then(userIsValid => {
if (userIsValid.error) {
// treat this as an error condition
throw responseHelper.getErrorResponse(ResponseErrorCode.joiValidatorError, userIsValid.error);
}
return this.authUser.registerUser(request.payload).then(results => responseHelper.getSuccessResponse(results, null));
}).catch(error => {
let message: ILogMessage = {
code: ResponseErrorCode.unknownError,
message: ResponseErrorCode.unknownError.toString(),
meta: error,
sourceFunction : 'ApiDataCreateCredentials: getRoute()'
};
this.logHelper.error(message);
// turn back into resolved promise with error as the result
return error;
}).then(reply); // always call reply
}
}
Things that did not seem ideal with your implementation:
Promise anti-pattern (creating an unnecessary wrapper promise).
Calling resolve() multiple times (the second one will be ignored, but it seems less than ideal to code it that way)
There does not seem to be any particular benefit to using async/await here. It seems to complicate the flow (in my opinion).
resolve() followed by throw is a real head scratcher when reading the code. Yes, one can eventually figure out what you're trying to do, but this is an odd way to do it. My scheme is a bit more semantic. if (userIsValid.error) is really just an error condition for you so coding it this way just makes that more obvious. Then, since you want ALL errors to proceed as if there was no error (after logging), we just make the .catch() handler return normally which allows the last .then() handler to always get called.
Other references related to your topic:
Can't throw error from within an async promise executor function
Is it an anti-pattern to use async/await inside of a new Promise() constructor?

What is the best way to wrap synchronous functions in to a promise

Let's say I have a synchronous function like path.join(). I want to wrap it into a Promise because I want exceptions to be handled within catch() block.
If I wrap it like below, I do not get an exception in the Promise's .catch() block. So I have to use if to check the return value for whether it's an error or not and then call resolve or reject functions. Are there any other solutions?
var joinPaths = function(path1,path2) {
return new promise(function (resolve, reject) {
resolve(path.join(path1, path2));
});
};
It is unclear why you would wrap a synchronous operation in a promise as that just makes it more difficult to use and synchronous operations can already be used within promise chains just fine.
The only two places I've seen it useful to make a promise out of something synchronous are to start a promise chain where subsequent operations will be async or when you are branching and one result of the branch is async promise and the other is synchronous. Then, in that case, you want to just return a promise in both cases so the caller has a consistent async interface no matter which branch is taken. Or, this could also occur with different implementations of a common API, one implementation which is synchronous and the other asynchronous. The API would be designed with an asynchronous interface and the synchronous implementation would probably wrap itself in a promise to fulfill the asynchronous contract of the common API.
Other than that, you should generally not make synchronous things async because it just unnecessarily complicates using them.
The simplest way I know of to make it into a promise would be this:
Promise.resolve(path.join(path1, path2)).then(function(path) {
// use the result here
});
Per your comments, inside a .then() handler, exceptions are already captured by the promise infrastructure and turned into a rejected promise. So, if you had this:
someAsyncOp().then(function(value) {
// some other stuff
// something that causes an exception
throw new Error("timeout");
}).catch(function(err){
console.log(err); // will show timeout
});
Then, that exception is already mapped into a promise rejection for you. Of course, if you want to handle the exception inside of the .then() handler (not turn the promise into a rejection), then you can just use a traditional try/catch around your synchronous operation to catch a local exception (no different than any other synchronous code). But, if you want the promise to reject if there's an exception inside the .then() handler, then that is all done for you automatically (a very nice feature of promises).
Not sure if it's the best way but it works. I came to StackOverflow to check if there's a better way.
new Promise((resolve, reject) => {
try {
resolve(path.join());
} catch (err) {
reject(err);
}
})
Very quick way is to wrap prepend a function with async keyword. Any async function returns a Promise
const sum = async (a, b) => a + b;
sum(1, 2).then(value => console.log(value))

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