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.
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();
I'm hoping someone can suggest a better method for what I'm trying to achieve.
While performing a rather long and complicated webflow using puppeteer, occasionally an error will occur that disrupts the actions I'm trying to take on the page. There's nothing I can do about it in these situations other than return a useful error. However it doesn't happen often enough to explicitly wait and try to catch it after each step in my code.
The solution I have is:
async function runCode() {
try {
const page = browser.open(url)
listenForError(page)
await longWebFlow()
} catch (err) {
return err.message
}
}
async function listenForError(page) {
await page.waitForXPath(errorMessageXPath)
throw new Error('Error found!')
}
try {
await runCode()
} catch (err) {
console.log(err.message)
// should print('Error found')
}
Obviously, the unawaited listenForError call won't be caught in the try/catch, but I also cant await the call, or else I'll never get to the main part of the code.
The code works to short-circuit the process and return, since the error occurred, but how can I refactor this to catch the error message?
It seems like you want to wait until either the error is thrown or the longWebFlow() finishes - basically doing both concurrently. That's a perfect use case for promises with Promise.race:
async function runCode() {
const page = browser.open(url)
await Promise.race([
listenForError(page),
longWebFlow(),
]);
}
You could also use Promise.all if you made longWebFlow cancel the listenForError and fulfill the promise.
Either way, since you can properly await the promises now, the error also will be caught in the try/catch, as it should.
If you can't await the async operation then the only other "follow up" is with callbacks like .then() and .catch(). For example, you can catch the error here:
listenForError(page).catch(e => console.log(e));
This won't await the operation, it's just supplying a callback for whenever that operation fails at whatever point in the future.
I recently ran into a Javascript problem catching errors and thus crashing when exception thrown.
funcReturnPromise().then().catch()
I had to change this to:
try {
funcReturnPromise().then()
} catch (e) {
...
}
Couldn't find a decent explanation for it, any JS wizards available to enlighten a JS peasant?
If funcReturnPromise() can throw synchronously (which functions that return promises should generally never do), then you do have to catch that synchronous exception with try/catch as you discovered when using regular .then().
This is one place where async functions can hep you. For example, if you declare funcReturnPromise as async, then the synchronous exception it throws will automatically become a rejected promise and the caller won't ever be exposed to a synchronous exception.
Or, if the caller (your code here) uses await, then you can catch both sycnhronous exceptions and rejected promises with the same try/catch.
So, for example, you could do this:
async function myFunc()
try {
let result = await funcReturnPromise();
console.log(result);
} catch (e) {
// this catches both a rejected promise AND
// a synchronously thrown exception
console.log(e);
}
}
Suppose I have an async function
static async update (id, data){
try {
//async function below
_DB.putsomedata()
return true
} catch(e){
//log errors
}
}
And although I don't need the result of _DB.putsomedata(), I still need to know if the update() function finished without errors.
I did some testing and noticed that the result will always return true (thats expected), but in case it fails, the the error is never caught in the catch block, and that raises an unhandled exception.
Can somebody explain the behaviour?
Assuming _DB.putsomedata is an async function, you need to await it. Otherwise its Promise will simply continue in the background, where it eventually fails and produces the error. Your code will have continued on ahead in the meantime and the try..catch block will have long been exited.
putsomedata function is asynchonious, so the return statment in compute just after marking putsomedata as computable (and not launch, as for synchronious function).
putsomedata return probably a promise, which will be resolved after, when cpu will be avaiable.
you can use .promise() .then() .catch() functions
I am busy working with some code that is responing in an unexpected way (to me). It involves handling Node.js promise exceptions.
I have the following function modified so that all it does is fail
function asynFunc() {
return new Promise(function(res, rej) {
rej("GENERIC ERROR");
});
}
The problem comes in when I am trying to call it and handle this error. If I do the following it works as I expect it to, the function fails and executes the catch which returns, stopping the last console log from executing
async function appStart() {
try {
await asyncFunc();
} catch(log) {
console.log(log);
return;
}
console.log("YOU SHOULD NOT SEE THIS");
}
appStart();
If I write the function as follows though, it does not seem to return, it still executes the last console log regardless of the await
async function appStart() {
await asyncFunc().catch(function(log) {
console.log(log);
return;
});
console.log("YOU SHOULD NOT SEE THIS");
}
If this is doing what I think it's doing then the return is returning from the function inside of the catch and not the appStart function itself, or I'm completely wrong in which case I have no idea why it's not returning.
Is there a way to use the second catch method and still have it return from the calling function?
In the second example, you are not returning from the outside function in the catch, you are returning from the catch callback:
await asyncFunc().catch(function(log) {
console.log(log);// ^
return; // | returns from that function
});
This has the effect of catching the error and moving on and returning a new promise resolving to undefined. There is no way to control the return of the outside function from inside the callback. You need to test the result of the async operation from the outside function, which leaves you with try/catch or explicitly testing the result after the promise resolves.
You're right. It's returning only inside the catch callback, not the outer async function. That's why it exits the catch callback and resolves, and then logs "YOU SHOULD NOT SEE THIS". Generally, it's very unreadable if you mix promise then and catch chaining along with async/await and try/catch. Pick one and stick with it, because mixing them may lead to the inability to catch and handle errors seamlessly.