This question already has answers here:
Try catch block is not catching nested callbacks
(2 answers)
Is it possible to catch exceptions thrown in a JavaScript async callback?
(7 answers)
Closed last year.
I am trying to catch an error caused by an async javascript callback function,
try{
setTimeout(()=>{
throw err
console.log("after throw")
}, 1000)
}catch(e){
console.log("caught");
}
But as many of you may know catch block is never executed, so what exactly is happening here?
I know I can achieve similar thing using promises and async/await,
async foo(){
try{
await setTimeoutPromise(1000);
}catch(e){
alert("caught");
}
}
When you use setTimeout, the callback gets pushed into the event loop (moved out of the JS Engine and into the realm of the runtime/browser) and your foo function exits immedeatly.
After the timeout AND once the stack is empty, the event loop will put the callback onto the stack and run it. That's why the try/catch and the callback are independent of each other. That's also why setTimeout(1000) does not mean in a second but not earlier than and somewhat close to one second.
See What the heck is the event loop anyway? | Philip Roberts | JSConf EU
These are two different things.
The first code won't catch the error, since it happens asynchronously, and try...catch will only catch synchronously thrown exceptions.
The second code will catch the error, because await 'synchronize' the code (makes it look and work like it would've been synchronous), and the error thrown only by await: if you haven't been used it, you would get only a rejected promise (You can't really throw anything from an async function!)
To make the first code working, move the try...catch inside the callback:
setTimeout(()=>{
try{
throw err
}catch(e){
console.log('catched')
}
console.log("after throw")
}, 1000)
Related
I would like to run this code with babel:
redisClientAsync.delAsync('key');
return await someOtherAsyncFunction();
inside an async function without await the first line. is this OK?
how else can I run something that I don't care?
Can I just fire the non-promisified function del('key',null) without a callback?
Yes, you can do that, and it will run the two asynchronous functions in parallel. You've just created a promise and thrown it away.
However, this means that when the promise is rejected you won't notice. You'll just get an unhandledRejection eventually which will crash your process if not handled.
Is this OK? How can I run something that I don't care?
Probably it's not OK. If you truly wouldn't care, you hadn't run it in the first place. So you should be clear and explicit what you care about (and what not):
do you want to wait? (for side effects)
do you need the result?
do you want to catch exceptions?
If you only want to wait and don't care for the result value, you can easily throw away the result:
void (await someAsyncFunction()); // or omit the void keyword,
// doesn't make a difference in an expression statement
If you don't care about exceptions, you can ignore them using
… someAsyncFunction().catch(function ignore() {}) …
You can throw that away, await it, do anything with it.
If you want the result, you have to await it. If you care about exceptions, but don't really want to wait, you may want to execute it in parallel with the following functions:
var [_, res] = await Promise.all([
someAsyncFunction(), // result is ignored, exceptions aren't
someOtherAsyncFunction()
]);
return res;
inside an async function without await the first line. is this OK?
Yes, there are cases where you'd want to do this which are perfectly reasonable. Especially where you don't care about the result - one example is an analytics tracking operation that should not interfere with business critical code.
how else can I run something that I don't care?
In many ways, however simply calling the promise function works. Your del without a callback would probably work in this case but some functions don't guard against not passing callbacks, so you can pass an empty function instead (.del('key', () => {})).
You do want to however make sure that you know about it failing, even if you don't want to disrupt the operation of code - so please consider adding a process.on("unhandledRejection', event handler to explicitly ignore these particular exceptions or suppress them via:
redisClient.delAsync('key').catch(()=>{});
Or preferably, something like:
redisClient.delAsync('key').catch(logErr);
From all the research I've made so far, I think it's fine to do it, as long as you guarantee that the function you are not awaiting for guarantees a way to handle its own errors in case that happens. For example, a try-catch wrapping the whole function body, like you see in the following snippet for the asyncFunction.
It doesn't matter if the function throws synchronously or asynchronously. It guarantees the your mainFunction will complete no matter what. That's the key point here.
If you don't guarantee that, you have to risks:
If it throws synchronously, your main function will not complete.
If it throws asynchronously, you'll get an unhandled excepction
// THIS IS SOME API CALL YOU DON'T WANT TO WAIT FOR
const mockAPI = () => {
console.log("From mockAPI");
return new Promise((resolve,reject) => {
setTimeout(() => reject("LATE THROW: API ERROR"), 500);
});
};
// THIS IS THE SOME ASYNC FUNCTION YOU CALL BUT NOT AWAIT FOR
const asyncFunction = async (syncThrow) => {
try {
console.log("Async function START");
if (syncThrow) throw new Error("EARLY THROW");
await mockAPI();
console.log("Async function DONE");
}
catch(err) {
console.log("From async function catch");
console.log(err.message || err);
return;
}
};
// THIS IS YOUR MAIN FUNCTION
const mainFunction = async (syncThrow) => {
try {
console.clear();
console.log("Main function START");
asyncFunction(syncThrow);
console.log("Main function DONE <<< THAT'S THE IMPORTANT PART");
}
catch(err) {
console.log("THIS WILL NEVER HAPPEN");
console.log(err);
}
};
<div>
<button onClick="mainFunction(true)">Sync throw</button>
<button onClick="mainFunction(false)">Async throw</button>
</div>
Not in Node.js.
Node does not wait for ever-pending Promises. If other tasks are already completed and there is nothing left in the event loop, the Node process will be terminated even though there exists pending promise.
For the following script, if someOtherAsyncFunction() get resolved in 5 seconds, but redisClientAsync.delAsync('key') takes 10 seconds to execute, the Node process will be terminated after 5 seconds in theory, before the first line is resolved.
async function doSomething() {
redisClientAsync.delAsync('key');
return await someOtherAsyncFunction();
}
await doSomething();
This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed last year.
When I have:
try {
array.forEach(items => {
//do something
//here something is going wrong
})
res.status(200).json({
success: true
})
} catch (err) {
res.status(400).json({
message: err.message
})
}
Error is not catching - I've got 200 response but I can see crashed app and on terminal I have error. But this error is not catched by catch(err) block.
But if I have For of loop I'm getting 400 response.
Erros is the same - in forEach it is not catched and response is ok but in for of loop erros is catched and I have 400 response with error.
Why? Why I can't catch error in forEach and send 400 res by catch block?
Short answer:
This is designed for JS forEach.
Longer one:
Note: There is no way to stop or break a forEach() loop other than by
throwing an exception. If you need such behavior, the forEach() method
is the wrong tool.
From MDN Web Docs Array.prototype.forEach()
BTW, it does not wait for promise as well.
forEach does not wait for promises. Make sure you are aware of the implications while using promises (or async functions) as forEach callback.
In sum,
This probably confuses many JS developers, but in design, this is how JS works.
For avoid the similar issues, try not using forEach, use for or while
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.
This question already has answers here:
How to deal with dangling promises
(2 answers)
Closed 3 years ago.
Currently I have code such as:
window.onerror = function() {
// Send some debug info about the defective error handling,
// so that it may be fixed
};
To handle otherwise uncaught exceptions. Unfortunately this does not work for an async function. For example, if we call fooasync() below:
async function fooasync() {
throw 'Catch me!';
}
Then the exception (if otherwise uncaught) won't be caught by the window.onerror catch-all...
I've done the requisite searching and have mostly come accross the answer, "it's not possible and anyway, it would be bad and you should catch it explicitly." I agree of course, but what if you have accidentally not wrapped an async function in a try...catch?
As an example of a valid use-case: I use the window.onerror handler to send me a debug email to let me know that something has gone horribly wrong for the person using my whatever. Great, at least I'm aware of the problem: Now I can fix the issue (and even fix the broken error-handling with an explicit try...catch). However, in the case of the async function, I have no way to even become aware of the issue (other than someone complaining that it's not working the way it's supposed to).
Is it possible to catch these async uncaught exceptions?
Similar to onerror, there is onunhandledrejection, which will be triggered if a Promise rejects and no handler is attached to it. Read on on MDN
I read a very informative blog (by PANU PITKÄMÄKI) post about why asynchronous exceptions are untracable in NodeJS. It stated that in NodeJS ansynchronous exceptions are untracable because the catch block simply doesn't exist in the stack of the event loop. For example:
try {
setImmediate(() => {
throw new Error();
});
} catch (e) {
// catch error.. doesn't work
}
I also read the following in the book "NodeJS design patterns:
Throwing inside an asynchronous callback will cause the exception to jump up the event loop and never be propagated to the next callback. In NodeJS this is an unrecoverable state and causes application to shut down.
Question:
From the book I get the impression that the event loop has a seperate stack and thus a seperate thread, is this true?