using try catch on error generated by asynchronous callback function
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
Catching Errors thrown by callback functions
The try-catch block in the code below does not work as long as
someFunction
is asynchronous
Of course it doesn't - someFunction
is asynchronous and returns a promise. try
/catch
cannot catch asynchronous errors (promise rejections) unless you await
that promise. If you don't call someFunction
from an async
function context, you will need to use the normal promise catch
method:
try {
await someFunction(()=> {
// ^^^^^
console.log("Callback has been called")
throw new Error("Some error");
});
} catch (error) {
console.log(error.message)
}
or
someFunction(() => {
console.log("Callback has been called")
throw new Error("Some error");
}).catch(error => {
console.log(error.message)
});
Of course there's no reason for that someFunction
to take a callback. You should rather call the someAsyncFunction
directly and use the promise that it returns for installing your callback
someAsyncFunction().then(() => {
console.log("Callback has been called")
throw new Error("Some error");
}).catch(error => {
console.log(error.message)
});
or just not using a callback at all with async
/await
:
try {
await someAsyncFunction()
console.log("'Callback' code has been called")
throw new Error("Some error");
} catch (error) {
console.log(error.message)
}
How to catch exception in async context
The Eventemitter
is fully sync
and there is no workaround for it. What you can do is to use the eventemitter2 module to replace the original Eventemitter
class. It is fully compatible with Eventemitter
and allows you to use async
actions.
var EventEmitter = require('eventemitter2');
const delaySync = function (ttd) {
return new Promise((resolve) => {
setTimeout(() => resolve(0), ttd);
});
};
const myEvent = new EventEmitter();
myEvent.on('something', async () => {
console.log('Event triggered');
// Just delay for 100ms and then throw
await delaySync(100);
throw new Error('Something happened');
// for async emitters pass promisify true.
}, { promisify: true });
describe('Events', () => {
it('should catch error', async () => {
await expect(myEvent.emit('something')).rejects.toThrow('Something happened');
});
}
Catching and returning an error from a callback that may be asynchronous
Realised that it's not necessary to chain the catch
immediately! So the following abomination allows me to determine if it is a promise and handle the error accordingly:
const y = (error, callback) => function () {
try {
const returnValue = callback(...arguments);
if (returnValue instanceof Promise) {
return returnValue.catch((callbackError) => {
return callbackError;
});
}
else {
return returnValue;
}
}
catch (callbackError) {
return callbackError;
}
};
If you're wondering why this function is useful, here's an example of what I'm using it for. It modifies the error (chaining it with another error, see VError
) before returning it:
const y = (error, callback) => function () {
try {
const returnValue = callback(...arguments);
if (returnValue instanceof Promise) {
return returnValue.catch((callbackError) => {
throw VError(error, callbackError);
});
}
else {
return returnValue;
}
}
catch (callbackError) {
throw VError(error, callbackError);
}
};
Note the return of the following as per the question:
const a = y(() => 42)(); // returns '42'
const b = y(async () => Promise.resolve(42))(); // returns 'Promise {42}'
const c = y(() => { throw new Error('Base'); })(); // throws VError
const d = y(async () => { throw new Error('Base'); })(); // throws VError
How to handle async callback promise rejection?
You shouldn't pass async
functions into things that don't expect them (unless you catch all errors, as you are with your app.post
callback). Instead, give yourself a wrapper for limiter.removeTokens
that returns a promise:
function removeTokens(limiter, id) {
return new Promise((resolve, reject) => {
limiter.removeTokens(id, (err, remainingRequest) => {
if (err) {
reject(err);
} else {
resolve(remainingRequest);
}
});
});
}
(You might also look into util.promisify
for that.)
Then:
app.post('/', async (req, res) => {
try {
...
await getAll(); // *** Or you might just use `removeTokens(limiter, 1)` directly here
function getAll() {
return removeTokens(limiter, 1);
}
} catch (error) {
console.log(error);
}
});
Here it is using removeTokens
directly:
app.post('/', async (req, res) => {
try {
...
await removeTokens(limiter, 1);
} catch (error) {
console.log(error);
}
});
Exceptions thrown in asynchronous javascript not caught
When you are throwing the error the try {}
block has been long left, as the callback is invoked asynchronously outside of the try/catch. So you cannot catch it.
Do whatever you want to do in case of an error inside the error callback function.
How to bubble up Exceptions from inside async/await functions in NodeJs?
In a nutshell, there is no way to propagate an error from the xml2js.parseString()
callback up to the higher code because that parent function has already exited and returned. This is how plain callbacks work with asynchronous code.
To understand the problem here, you have to follow the code flow for xml2js.parseString()
in your function. If you instrumented it like this:
app.post('/ams', async (req, res) => {
try {
console.log("1");
xml2js.parseString(xml, async (err, json) => {
console.log("2");
if (err) {
throw new XMLException();
}
// assume many more clauses here that can throw exceptions
res.status(200);
res.send("Data saved")
});
console.log("3");
} catch (err) {
if (err instanceof XML2JSException) {
res.status(400);
message = "Malformed XML error: " + err;
res.send(message);
}
}
console.log("4");
});
Then, you would see this in the logs:
1 // about to call xml2js.parseString()
3 // after the call to xml2js.parseString()
4 // function about to exit
2 // callback called last after function returned
The outer function has finished and returned BEFORE your callback has been called. This is because xml2js.parseString()
is asynchronous and non-blocking. That means that calling it just initiates the operation and then it immediately returns and the rest of your function continues to execute. It works in the background and some time later, it posts an event to the Javascript event queue and when the interpreter is done with whatever else it was doing, it will pick up that event and call the callback.
The callback will get called with an almost empty call stack. So, you can't use traditional try/catch
exceptions with these plain, asynchronous callbacks. Instead, you must either handle the error inside the callback or call some function from within the callback to handle the error for you.
When you try to throw inside that plain, asynchronous callback, the exception just goes back into the event handler that triggered the completion of the asynchronous operation and no further because there's nothing else on the call stack. Your try/catch
you show in your code cannot catch that exception. In fact, no other code can catch that exception - only code within the exception.
This is not a great way to write code, but nodejs survived with it for many years (by not using throw
in these circumstances). However, this is why promises were invented and when used with the newer language features async/await
, they provide a cleaner way to do things.
And, fortunately in this circumstance xml2js.parseString()
has a promise interface already.
So, you can do this:
app.post('/ams', async (req, res) => {
try {
// get the xml data from somewhere
const json = await xml2js.parseString(xml);
// do something with json here
res.send("Data saved");
} catch (err) {
console.log(err);
res.status(400).send("Malformed XML error: " + err.message);
}
});
With the xml2js.parseString()
interface, if you do NOT pass it a callback, it will return a promise instead that resolves to the final value or rejects with an error. This is not something all asynchronous interfaces can do, but is fairly common these days if the interface had the older style callback originally and then they want to now support promises. Newer interfaces are generally just built with only promise-based interfaces. Anyway, per the doc, this interface will return a promise if you don't pass a callback.
You can then use await
with that promise that the function returns. If the promise resolves, the await
will retrieve the resolved value of the promise. If the promise rejects, because you awaiting the rejection will be caught by the try/catch. FYI, you can also use .then()
and .catch()
with the promise, but in many cases, async
and await
are simpler so that's what I've shown here.
So, in this code, if there is invalid XML, then the promise that xml2js.parseString()
returns will reject and control flow will go to the catch
block where you can handle the error.
If you want to capture only the xml2js.parseString()
error separately from other exceptions that could occur elsewhere in your code, you can put a try/catch
around just it (though this code didn't show anything else that would likely throw an exception so I didn't add another try/catch
). In fact, this form of try/catch
can be used pretty much like you would normally use it with synchronous code. You can throw up to a higher level of try/catch
too.
A few other notes, many people who first start programming with asynchronous operations try to just put await
in front of anything asynchronous and hope that it solves their problem. await
only does anything useful when you await
a promise so your asynchronous function must return a promise that resolves/rejects when the asynchronous operation is complete for the await
to do anything useful.
It is also possible to take a plain callback asynchronous function that does not have a promise interface and wrap a promise interface around it. You pretty much never want to mix promise interface functions with plain callback asynchronous operations because error handling and propagation is a nightmare with a mixed model. So, sometimes you have to "promisify" an older interface so you can use promises with it. In most cases, you can do that with util.promisify()
built into the util library in nodejs. Fortunately, since promises and async/await
are the modern and easier way to do asynchronous things, most newer asynchronous interfaces in the nodejs world come with promise interfaces already.
Related Topics
How to Remove Leading and Trailing White Spaces from a Given HTML String
Get Position/Offset of Element Relative to a Parent Container
Getting a List of Associative Array Keys
How to Make a Div Element Editable (Like a Textarea When I Click It)
Converting JSON Results to a Date
How to Replace a Regex Substring Match in JavaScript
How Dangerous Is It in JavaScript, Really, to Assume Undefined Is Not Overwritten
What's a Good Tool to Screen-Scrape with JavaScript Support
Reactjs Syntheticevent Stoppropagation() Only Works with React Events
Array Like Objects in JavaScript
Microsecond Timing in JavaScript
How to Access Dom Elements in Electron
Sort Object Properties and JSON.Stringify
Get Decimal Portion of a Number with JavaScript
How to Get the <Html> Tag HTML with JavaScript/Jquery
Javascript: Call a Function After Specific Time Period