Break out of Async Function Within a Catch Block - javascript

I have the following function:
async myFunction() {
await this.somePromiseFunction().then(
() => alert('Promise Complete!'),
() => {
throw new Error('Error');
}
).catch(() => {
alert('End the Function Here');
});
alert('Do not get called if error caught above.');
await this.anotherPromiseFunction().then(
() => alert('Promise Complete!'),
() => {
throw new Error('Error');
}
).catch(() => {
alert('End the Function Here');
});
}
I would like it such that when an error is caught in the promise return handler that it ends the asynchronous function as I do not want it to continue in that case.

Instead of mixing await with .then(), just await each asynchronous function call directly in a try block and deal with the error appropriately.
If the asynchronous function returns a rejected promise, await will cause the rejection to throw from the try block and be caught, skipping the rest of the control flow inside the try.
const asyncFactory = label => async () => {
await new Promise(resolve => { setTimeout(resolve, 1000); });
if (Math.random() < 0.25) {
throw new Error(`${label} Error`);
}
console.log(`${label} Complete!`);
};
const somePromiseFunction = asyncFactory('somePromiseFunction');
const anotherPromiseFunction = asyncFactory('anotherPromiseFunction');
async function myFunction() {
try {
console.log('Start myFunction here');
await somePromiseFunction();
await anotherPromiseFunction();
} catch (error) {
console.log('Error caught:', error.message);
} finally {
console.log('End myFunction here');
}
}
myFunction();
You can actually achieve the equivalent without using async and await, and you don't need to nest your promises to do so:
const asyncFactory = label => () => {
return new Promise(resolve => {
setTimeout(resolve, 1000);
}).then(() => {
if (Math.random() < 0.25) {
throw new Error(`${label} Error`);
}
console.log(`${label} Complete!`);
});
};
const somePromiseFunction = asyncFactory('somePromiseFunction');
const anotherPromiseFunction = asyncFactory('anotherPromiseFunction');
const oneMorePromiseFunction = asyncFactory('oneMorePromiseFunction');
function myFunction() {
console.log('Start myFunction here');
return somePromiseFunction().then(() => {
return anotherPromiseFunction();
}).then(() => {
return oneMorePromiseFunction();
}).catch(error => {
console.log('Error caught:', error.message);
}).finally(() => {
console.log('End myFunction here');
});
}
myFunction();
Do note that Promise.prototype.finally() is actually part of ECMAScript 2018, so if the browser supports it natively, it will also already support async and await. However, it can be polyfilled while async and await cannot.

Related

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');
})();

How to throw out of then-catch block?

I am currently figuring out how to throw an Exception out of a then catch block. I want to get into the catch that is inside the errorHandler() function.
const errorHandler = function () {
try {
thisFunctionReturnsAnError().then(response => {
console.log(response);
});
} catch (e) {
console.log(e); //How to trigger this?
}
};
const thisFunctionReturnsAnError = function () {
return3()
.then(value => {
throw new Error('This is the error message.');
})
.catch(err => {
//return Promise.reject('this will get rejected');
throw err;
//this one should somehow got to the catch that is located in the errorHandler() function. How to do this?
//I know that this 'err' will be in the next catch block that is written here. This is not what i want.
});
};
const return3 = async function () {
return 3;
};
errorHandler();
I searched a while on stackoverflow but nothing helped me. I am sure that this question got asked often but I could not find the answer, sorry for that.
EDIT:
added here another version of the code but it still does not work
const errorHandler = async function () {
try {
thisFunctionReturnsAnError()
.then(response => console.log(response))
.catch(err => console.log(err));
} catch (e) {
//console.log(e); //How to trigger this?
}
};
const thisFunctionReturnsAnError = function () {
return3()
.then(value => {
throw new Error('This is the error message.');
})
.catch(err => {
return Promise.reject(`Message is: ${err}`);
});
};
const return3 = async function () {
return 3;
};
errorHandler();
I will get the following error message:
Uncaught (in promise) Message is: Error: This is the error message.
Your code can't be executed, because "thisFunctionReturnsAnError" is not returning an Promise. That means that you can't call "then" on the return value.
thisFunctionReturnsAnError().then(response => { // will not work
Why not always use a promise?
const errorHandler = function () {
thisFunctionReturnsAnError()
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log('errorHandler: Handle the error.');
});
};
const thisFunctionReturnsAnError = function () {
return return_Three()
.then((value) => {
throw new Error('This is the error message.');
})
.catch((err) => {
//return Promise.reject('this will get rejected');
throw err;
//this one should somehow got to the catch that is located in the errorHandler() function. How to do this?
//I know that this 'err' will be in the next catch block that is written here. This is not what i want.
});
};
const return_Three = async function () {
return 3;
};
errorHandler();
/*****JUST ANOTHER SYNTAX*******/
const secondErrorHandler = async function () {
try {
await thisFunctionReturnsAnError();
} catch (error) {
console.log('secondErrorHandler: Handle the error.');
}
};
secondErrorHandler();
You cannot handle promise rejections with synchronous try/catch. Don't use it, use the promise .catch() method like in your thisFunctionReturnsAnError function.
You can handle promise rejections with try/catch when using async/await syntax (which you already do, albeit unnecessarily, in return3):
async function errorHandler() { /*
^^^^^ */
try {
const response = await thisFunctionReturnsAnError();
// ^^^^^
console.log(response);
} catch (e) {
console.log(e); // works
}
}

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 combine 2 Javascript functions into one?

I have the following function, which I want to re-use as an "action" template and pass another function as a parameter, which will be the action function, to be executed in the middle of it.
QUESTION
Is it possible? How can I do it?
Note that the "action" is asynchronous and also I'm using React.
function templateAction(action) {
try {
setLoading(true);
setError(null);
// DO SOMETHING
action();
setLoading(false);
}
catch(err) {
console.log(err);
setError(err);
setLoading(false);
}
}
And inside that action() call should execute the following function:
async function getBlogPost() {
const querySnapshot = await firebase.firestore().collection('blog').where('slug','==',props.match.params.slug).get();
console.log(querySnapshot.docs);
if (querySnapshot.docs.length === 0) {
throw 'ERROR: BlogPost not found...';
} else if (querySnapshot.docs.length > 1) {
throw 'ERROR: More than 1 blogPost found...';
}
const blogPostData = querySnapshot.docs[0].data();
setFirestoreID(querySnapshot.docs[0].id);
setBlogPost(blogPostData);
}
SNIPPET
I think that I've built the behavior that I need (run snippet). Can I do it simpler than that? It seems a lot of boilerplate.
async function templateAction(action) {
try {
console.log('Template Action BEFORE calling action');
await action();
console.log('Template Action AFTER calling action');
}
catch(err) {
console.log(err);
}
}
function action() {
return new Promise(async (resolve,reject) => {
console.log('I am sync from action function BEFORE async call');
await mockAPI();
console.log('I am sync from action function AFTER async call');
resolve();
});
}
function mockAPI() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log('I am from async mockAPI call');
resolve();
},1500);
});
}
templateAction(action);
You can pass by anonymous function like this: https://codesandbox.io/embed/awesome-mayer-52ix7
function templateAction(action) {
action();
}
let test = function() {
alert("test");
};
templateAction(test);
For the async part, maybe you will need to make promise, depending if you need a confirmation for the action.
With reactJS you can make this more simple by using scope sending properties and getting using "this.props"
Here is my final code, with error catching using try catch blocks.
It works, though I think it hurts readability.
async function templateAction(action) {
try {
console.log('Template Action BEFORE calling action');
await action();
console.log('Template Action AFTER calling action');
}
catch(err) {
console.log('I was caught and logged');
console.log(err);
}
}
function action() {
return new Promise(async (resolve,reject) => {
try {
console.log('I am sync from action function BEFORE async call');
await mockAPI();
console.log('I am sync from action function AFTER async call');
resolve();
}
catch(err) {
console.log('I was caught');
reject(err);
}
});
}
function mockAPI() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log('I am from async mockAPI call');
resolve();
},1500);
});
}
templateAction(action);

How to resolve promises and catch an error

I am trying to user webgazer.js where my code basically checks to see whether the webgazer is initialized and when it is initialized it resolves a promise which dispatches an action. This works however if for example there is no webcam I need to throw an error. The error in my code never gets called.
Here is my code
export function detectJsCamera() {
return async(dispatch) => {
dispatch({type: types.JS_DETECTING_CAMERA});
try {
await detectCamera();
await dispatch({type: types.JS_CAMERA_DETECTED});
} catch (error) {
await dispatch({type: types.CAMERA_DETECTION_FAILED, error: error.message});
throw error;
// this.props.history.push('/setup/positioning')
};
}
}
const detectCamera = () => new Promise((resolve, reject) => {
const checkIfReady = () => {
if (webgazer.isReady()) {
resolve('success');
} else {
console.log('called')
setTimeout(checkIfReady, 100);
}
}
setTimeout(checkIfReady,100);
});
You will need to reject in order to throw an exception like below
const detectCamera = () => new Promise((resolve, reject) => {
const checkIfReady = () => {
if (webgazer.isReady()) {
resolve('success');
} else {
console.log('called');
reject("some error");
}
}
setTimeout(checkIfReady,100);
});
You need to call reject() in your detectCamera method when your webgazer is not initialised then it would be caught in your catch block in detectJsCamera method.

Categories