Throw custom errors async/await try-catch - javascript

Let's say I have a function like this -
doSomeOperation = async () => {
try {
let result = await DoSomething.mightCauseException();
if (result.invalidState) {
throw new Error("Invalid State error");
}
return result;
} catch (error) {
ExceptionLogger.log(error);
throw new Error("Error performing operation");
}
};
Here the DoSomething.mightCauseException is an asynchronous call that might cause an exception and I'm using try..catch to handle it. But then using the result obtained, I might decide that I need to tell the caller of doSomeOperation that the operation has failed with a certain reason.
In the above function, the Error I'm throwing is caught by the catch block and only a generic Error gets thrown back to the caller of doSomeOperation.
Caller of doSomeOperation might be doing something like this -
doSomeOperation()
.then((result) => console.log("Success"))
.catch((error) => console.log("Failed", error.message))
My custom error never gets here.
This pattern can be used when building Express apps. The route handler would call some function which might want to fail in different ways and let the client know why it failed.
I'm wondering how this can be done? Is there any other pattern to follow here? Thanks!

Just change the order of your lines.
doSomeOperation = async() => {
let result = false;
try {
result = await DoSomething.mightCauseException();
} catch (error) {
ExceptionLogger.log(error);
throw new Error("Error performing operation");
}
if (!result || result.invalidState) {
throw new Error("Invalid State error");
}
return result;
};
Update 1
Or you could create custom errors as below.
class MyError extends Error {
constructor(m) {
super(m);
}
}
function x() {
try {
throw new MyError("Wasted");
} catch (err) {
if (err instanceof MyError) {
throw err;
} else {
throw new Error("Bummer");
}
}
}
x();
Update 2
Mapping this to your case,
class MyError extends Error {
constructor(m) {
super(m);
}
}
doSomeOperation = async() => {
try {
let result = await mightCauseException();
if (result.invalidState) {
throw new MyError("Invalid State error");
}
return result;
} catch (error) {
if (error instanceof MyError) {
throw error;
}
throw new Error("Error performing operation");
}
};
async function mightCauseException() {
let random = Math.floor(Math.random() * 1000);
if (random % 3 === 0) {
return {
invalidState: true
}
} else if (random % 3 === 1) {
return {
invalidState: false
}
} else {
throw Error("Error from function");
}
}
doSomeOperation()
.then((result) => console.log("Success"))
.catch((error) => console.log("Failed", error.message))

You can simply use throw instead of using Error constructor
const doSomeOperation = async () => {
try {
throw {customError:"just throw only "}
} catch (error) {
console.log(error)
}
};
doSomeOperation()

Related

What is the difference between throw Error(error) and throw error

What is the difference between:
try {
const result = await hello();
} catch (error) {
throw error;
}
and
try {
const result = await hello();
} catch (error) {
throw Error(error);
}
Also
Is the second one necessary? It seems like you are just wrapping an error with an Error Object. Which one is preferred? Please help me understand.
It's possible that the value that the Promise rejected with was not an error object, but something else:
(async() => {
try {
const result = await Promise.reject(5);
} catch (error) {
console.log(error);
console.log(typeof error);
}
})();
Doing
throw Error(error);
makes sure that the value being thrown is definitely an Error object, which could be important if the thrown value is examined later and is expected to be such an object. You wouldn't want, for example, for undefined or null to be thrown (strange, I know, but not impossible) and for accessing a property of that to then throw at the point where you're catching for real.
const hello = () => new Promise((resolve, reject) => {
reject();
});
(async() => {
try {
const result = await hello();
} catch (error) {
throw error;
}
})()
.catch((error) => {
console.log('The error message was:');
console.log(error.message);
});

escape loop using an error from async function?

let me explain what I mean using an example
async function async_function(){
await new Promise(r=>setTimeout(r,3000));
throw 'task completed'
}
async function do_something_meanwhile() {
await new Promise(r => setTimeout(r, 500));
console.log(Math.floor(Math.random()*10));
}
(async ()=>{
try {
async_function(); //this returns an error after a while
while (...)
await do_something_meanwhile();
} catch (err) { console.log('exited with error:',err) }
console.log('moving on');
})();
I'm trying to run an async function and after it is complete immediately terminate the loop,
the best way I could think of (without any time delay) was to send an error
but it gives this error instead of moving on after it's done:
node:internal/process/promises:246
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: This error originated either by throwing
inside of an async function without a catch block,
or by rejecting a promise which was not handled with
.catch(). The promise rejected with the reason "task
completed".] {
code: 'ERR_UNHANDLED_REJECTION'
}
is there a way around this or a better to achieve the desired effect?
You can handle rejection by setting an error variable that you can check in the loop:
try {
let error;
async_function()
.catch(err => error = err);
while (...) {
if (error) {
throw error;
}
await do_something_meanwhile();
}
} catch (err) {
console.log('exited with error:',err)
}
If you need to proactively tell do_something_meanwhile to terminate as well, you could use an AbortController and pass its signal to do_something_meanwhile.
try {
let error;
const controller = new AbortController();
const { signal } = controller;
async_function()
.catch(err => {
error = err;
controller.abort();
});
while (...) {
if (error) {
throw error;
}
await do_something_meanwhile(signal);
}
} catch (err) {
console.log('exited with error:',err)
}
I think if I were doing that, I might subclass AbortController so I can put the error in it:
class AbortContollerWithError extends AbortController {
abort(error) {
this.error = error;
super.abort();
}
}
then:
try {
const controller = new AbortController();
const { signal } = controller;
async_function()
.catch(err => {
controller.abort(err);
});
while (...) {
if (signal.aborted) {
throw controller.error;
}
await do_something_meanwhile(signal);
}
} catch (err) {
console.log('exited with error:',err)
}
...or something along those lines.
You asked how you'd use the signal in do_something_meanwhile, and suggested in a comment that you're really using a timer in it. That's where the signal's abort event comes in handy, you can use that to settle the promise early:
async function do_something_meanwhile(signal) {
let cancelError = {};
try {
await new Promise((resolve, reject) => {
const timer = setTimeout(resolve, 500);
signal.addEventListener("abort", () => {
clearTimeout(timer);
cancelError = new Error();
reject(cancelError);
});
});
console.log(Math.floor(Math.random() * 10));
} catch (error) {
if (error === cancelError) {
// Probably do nothing
} else {
// Something else went wrong, re-throw
throw error;
}
}
}
Promise.all can run async_function and do_something_meanwhile in parallel mode.
While Promise/A doesn't have a cancel method, you can define a stopFlag, and check it in do_something_meanwhile function and the while loop.
let stopFlag = false
async function async_function() {
await new Promise(r=>setTimeout(r, 3000));
throw 'task completed'
}
async function do_something_meanwhile() {
await new Promise(r => setTimeout(r, 500));
if (!stopFlag) {
console.log(Math.floor(Math.random() * 10));
}
}
(async()=>{
try {
await Promise.all([
async_function().catch((err) => {
stopFlag = true
throw err
}), // this returns an error after a while
(async () => {
while (!stopFlag)
await do_something_meanwhile();
})()
])
} catch (err) {
console.log('exited with error:', err)
}
console.log('moving on');
})();

NodeJS use errors for specific cases

Im using the following function that works ok, However I have some issue with the error handling
e.g.
I want to catch generic error for all the functions
If fn1 or fn2 returns any error the function should throw 'generic error occurred`
Here is the what I need
if getData2 doesn’t have specific property value (see the if) return a uniq error uniq error occurred and not the general error...
This is working example
https://jsfiddle.net/8duz7n23/
async function func1() {
try {
const data = await getData1()
console.log(data)
const data2 = await getData2();
if (!data2.url === !"https://test2.com") {
throw new Error("uniq error occurred ")
}
return data2.url
} catch (e) {
throw new Error("generic error occurred ")
}
}
async function getData1() {
return "something"
}
async function getData2() {
return {
url: "http://test.com"
}
}
func1().then(result => {
console.log(result);
}).catch(error => {
console.log(error);
});
Now If I throw the uniq error the catch will throw the general error.
I want that in case func2 will have some error it still throw away
a general error but just if doenst have the right url,
trow all the way up the uniq error ...
Is there any cleaner way to do it in nodejs?
I dont want to use an if statement for messages in the catch etc...
I want that the will thrown to the upper level function and not to the catch
In order to propagate error to the "upper level", you should have to throw it again in catch block.
You can add your custom error type and check if the error is what you're looking for or not.
class UrlMismatchError extends Error {
name="UrlMismatchError"
}
async function func1() {
try {
const data = await getData1()
console.log(data)
const data2 = await getData2();
if (data2.url !== "https://test2.com") {
throw new UrlMismatchError("uniq error occured");
}
return data2.url
} catch (e) {
if (e instanceof UrlMismatchError) {
throw e;
} else {
// handle generic errors
throw new Error("generic error occured");
}
}
}
async function getData1() {
return "something"
}
async function getData2() {
return {
url: "http://test.com"
}
}
func1().then(result => {
console.log(result);
}).catch(error => {
console.error(error.message);
});
I dont want to use an if statement for messages in the catch etc...
I want that the will thrown to the upper level function and not to the catch
No you cannot do that, because javascript is not java, so you cannot do such thing as the follows:
// the code snippet below wont work
try {
doSomething();
} catch(UrlMismatchError e) {
// propagate error to the upper level
throw e;
} catch(Error e) {
// handle all the other errors
console.log(e);
}

Confusion around 'nested' try/catch statements in Javascript

Essentially I have an async function containing a try/catch that calls another async function also containing a try catch, and I'm getting a bit confused about how to properly implement what I'm doing. Some "pseudocode" showing my current implementation:
const main = async () => {
try {
const test = await secondFunc();
console.log(test);
} catch(err) {
console.log('Found an error!');
console.log(err);
}
const secondFunc = async () => {
try {
await performSomeRequestExample();
} catch(err) {
if (err.x === 'x') {
doSomething();
} else {
//********
throw err;
//********
}
}
So what I'm trying to do is get the throw(err) (surrounded by the asterisks) to be caught by the catch in main() which will also call the console.log('Found an error!'), but what currently happens is the error is thrown from secondFunc(), the catch in main() is never hit and I get an unhandled promise rejection.
Any guidance on what I'm doing wrong?
My advice is to minimize using try/catch unless absolutely necessary. With async functions (or any functions that return a Promise object) you can usually simplify things by not worrying about try/catch blocks unless you need to do something specific with certain errors. You can also use .catch rather than try/catch blocks to make things easier to read.
For example your code above could be written like this:
const main = async () => {
const test = await secondFunc().catch(err => {
console.log("Found an error from secondFunc!", err);
throw err; // if you want to send it along to main's caller
});
if (test) {
console.log("Test", test);
}
};
const secondFunc = () => {
return performSomeRequestExample().catch(err => {
if (err.x === "x") {
doSomething();
} else {
throw err;
}
});
};
const performSomeRequestExample = () => Promise.reject("bad");
main().then(
() => console.log("worked"),
err => console.log("failed from main", err)
);
In secondFunc we don't need to use async since we can just return the promise coming back from performSomeRequestExample and handle any failures in the .catch.
You should use
const secondFunc = async () => {
performSomeRequestExample().then(res =>{
console.log(res);
})
.catch(err => {
console.log(err);
}
)
Add a return before the await of performSomeRequestExample.
const secondFunc = async () => {
try {
return await performSomeRequestExample();
} catch (err) {
if (err.x === 'x') {
console.log('x');
} else {
throw err;
}
}
}
or you can also use .catch() after the awaited function.
Another solution can be like this
const main = async() => {
try {
const test = await secondFunc();
console.log(test);
} catch(err) {
console.log('Found an error!');
console.log(err);
}
}
const secondFunc = async () => {
//return await performSomeRequestExample(); //for success
return await performSomeRequestExample(2); //for error
}
const performSomeRequestExample = async(abc=1) => {
return new Promise(function(resolve,reject){
if(abc ==1){
setInterval(resolve("yes"),400);
}else{
setInterval(reject("opps"),400);
}
});
}
main();
Test this code at this link:
https://repl.it/repls/JoyfulSomberTelevision

how to handle new Error() in node.js using ES6 Symbol?

I am creating a endpoint in node.js using ES6 Symbol. Example
// ES6 Symbol Method
const taskCreationMethod = {
[Symbol.taskMethod]() {
return {
storeCheckFunc: async function(storeId, employeeId) {
let store = await resourceModel["stores"].findById(storeId).populate(references["stores"]);
if(!store) {
return new Error("Store not found");
}
let employeeCheck = _.find(store.employees, (empObj) => {
return empObj._id == employeeId
})
if(!employeeCheck) {
return new Error("Employee not found");
}
return employeeCheck;
}
};
}
}
//end point
export const taskCreation = async(req, res) => {
const storeCheck = await taskCreationMethod[Symbol.taskMethod]().storeCheckFunc(req.body.store, req.body.assigned_to);
// here How can I handle return with Error Response?
}
You need to throw that error not just return it if you want to use the mechanisms of error handling. The thrown error will become a rejected promise which you can then handle with .catch() directly on the promise or with try/catch if you are using it in an async function. Here's a simplified example:
function populate() {
// always resolves to undefined
return Promise.resolve(undefined)
}
const taskCreationMethod = {
someMethod() {
return {
storeCheckFunc: async function() {
let store = await populate() // always resolves undefined
if (!store) { // so it always fails
throw new Error("Store not found"); // throw error
}
}
};
}
}
// regular promise then().catch()
taskCreationMethod.someMethod().storeCheckFunc()
.then(res => console.log(res))
.catch(err => console.log("Error:", err.message)) // catch
// OR … async function
async function runit() {
try {
let s = await taskCreationMethod.someMethod().storeCheckFunc()
} catch (err) {
console.log("Error:", err.message)
}
}
runit()

Categories