Raise exception from async failure in JavaScript Promise - javascript

When some asynchronous call within my Promise definition throws an Error, I can't seem to bubble it up to my catch handlers.
var promise = new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('about to fail!');
throw new Error('async FAIL');
}, 1000)
throw new Error('synchronous fail!');
}).catch(function(err) {
console.log('catch1!!!');
throw err;
});
promise.catch(function(err) {
console.log('CAUGHT!');
console.log(err.stack);
});
This code effectively catches the synchronous failure. But, is there any way to catch the async failure within the Promise rubric? Am I missing something fundamental about how I'm trying to use promises?

You can't catch exceptions thrown asynchronously with promises - or in any other way except the global platform error handler. Promises are not magic of course.
That's why you have to perform promisification at the lowest level possible - since promise handlers implicitly wrap calls with try/catch.

It's possible with closures if the async code has success/failure callbacks. The issue is keeping a reference to the resolve/reject functions in a different call stack:
function mySuccessCallback(resolve) {
return function() {
resolve();
};
}
function myFailureCallback(reject) {
return function() {
reject();
};
}
var promise = new Promise(function(resolve, reject) {
var thirdPartyTool = new ThirdPartyTool();
thirdPartyTool.executeAsync(
mySuccessCallback(resolve),
myFailureCallback(reject));
});
If there aren't success/failure callbacks then how can your code possibly know when theirs has completed an asynchronous action? unless of course your promise object is only promising that the async call will be executed, and nothing more, then it doesn't matter when the async action finishes. But the only way to tell if another bit of code has thrown an exception is to use the global error handler (https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror), because as another person said it's bubbling up in a totally different call stack. But even then how can you generically guarantee that the error being caught is caused by that particular async call and not another?

Related

Node JS: Not calling promise reject() callback stops the execution of the program

It seems I misunderstand the Promise mechanism. In the following code:
async function CreateSession()
{
var sessionId = await new Promise((resolve, reject) => {
request(options, function (error, response, body) {
if (!error && response.statusCode == 200) {
log.debug("Session created successfully")
resolve(body["session-id"]);
}
else
{
log.error("Failed to create session:");
log.error(error);
// reject(error);
}
});
});
log.debug(`Session Id: ${sessionId}`);
return sessionId;
}
The reject() is commented on purpose to understand the mechanism.
When calling the above CreateSession() function as follows and making sure an error is happenning:
async function MyFunction()
{
var sessionId = await CreateSession();
AnotherCode();
}
The entire program stops, the code after await in CreateSession is never reached, AnotherCode() is never called, and even if I put a handler for uncaught exception it doesn't catch any error.
My questions:
Why does the program stops when reject() is not called?
If it's an error why it is not called by the handler for uncaught exceptions?
How do I ignore the an error when a promise fails to run its code?
Why does the program stops when reject() is not called?
Because of the await. The program waits for the promise to either resolve or reject. It can't detect somehow that the code inside the promise constructor has finished executing (even if it could - what should be the "standard" behavior?) and there is no more code to come.
From the MDN:
An async function can contain an await expression that pauses the execution of the async function to wait for the passed Promise's resolution, then resumes the async function's execution and evaluates as the resolved value.
For some "under the hood" insights, you might want to look at the ES5 code generated by transpilers like Babel (IIRC it uses generators to handle this).
If it's an error why it is not called by the handler for uncaught exceptions?
The program doesn't know that error is an error if it's neither thrown nor passed using reject.
How do I ignore the an error when a promise fails to run its code?
Either resolve (instead of reject) or reject and do the classical try/catch around the await code
For a start you're mixing 2 concepts that achieve the same thing, the creation of a Promise. You are mixing async/await and new Promise.
That makes it actually like you have 2 chained Promises.
Why does the program stops when reject() is not called?
Because you are returning a Promise and that by itself demands that either a resolve or a reject callbacks are called.
If it's an error why it is not called by the handler for uncaught exceptions?
There is no actual error, the only thing that happens is that your if clause is not met.
How do I ignore the an error when a promise fails to run its code?
Again, the Promise does not fail to run it's code, you're failing to understand the concept.
To make it easier either use something like :
function CreateSession(){
return new Promise((resolve, reject) => {
if( <yourconditionhere> ){
resolve(res);
}else{
reject(error);
}
});
}
CreateSession
.then((res) => {
// code to execute in case of success (resolve)
// receiving as argument the argument of the resolve call.
AnotherCode();
})
.catch( error => {
// code to execute in case of non-success (reject)
// receiving as argument the argument of the reject call.
});
or
async function CreateSession(){
let res = await request(options, function (error, response, body) {
if( <yourconditionhere> ){
return ...;
}else{
return ...;
}
}
return res;
}
async function MyFunction()
{
var sessionId = await CreateSession();
AnotherCode();
}

Early returning inside an asynchronous function

Assume the scenario where you have to call an asynchronous function, but you are not really interested about success/failure situation of that function. In that case what are the pros and cons in following two patterns stated below with respect to call stack, callback queue and event loop
Pattern-1
async setSomething() {
try {
set(); // another async function
} catch (err) {
// log the error here
}
return true;
}
Pattern-2
async setSomething() {
try {
await set(); // another async function
} catch (err) {
// log the error here
}
return true;
}
Pattern 1 does not catch any errors that may occur during asynchronous operations in the set function - any errors will result in an unhandled Promise rejection, which should be avoided. Pattern 1 will only catch errors that occur during set's synchronous operations (such as, when setting up a fetch request), which are not likely to occur in most situations.
Example:
// open your browser's console to see the uncaught rejection
const set = () => new Promise((_, reject) => setTimeout(reject, 500));
async function setSomething() {
try {
set(); // another async function
} catch (err) {
console.log('err');
}
return true;
}
setSomething();
So, pattern 2 is likely preferable. If you don't care about the result of the asynchronous call, then don't await or call .then when you call setSomething().
Or, for something this simple, you might consider using Promise methods only, no async function needed:
const setSomething = () => set()
.catch((err) => {
// log the error here
});
This answer is a rather unconventional advice to the question than an actual answer to the examples posted by OP.
not really interested about success/failure situation of that function
If the above statement is the case, then it means, the return is not dependent on the result of the async invocation.
When you're not bothered about the return of the async invocation, it's better off to not use async/await or any type of promise at all. You could just invoke the function like any other function and process with the rest of the code.

Why JS Promise's async executor and async handler methods behave differently? [duplicate]

I've been trying to get a conceptual understanding of why the following code doesn't catch the throw. If you remove the async keyword from the new Promise(async (resolve, ... part then it works fine, so it has to do with the fact that the Promise executor is an async function.
(async function() {
try {
await fn();
} catch(e) {
console.log("CAUGHT fn error -->",e)
}
})();
function fn() {
return new Promise(async (resolve, reject) => {
// ...
throw new Error("<<fn error>>");
// ...
});
}
The answers here, here, and here repeat that "if you're in any other asynchronous callback, you must use reject", but by "asynchronous" they're not referring to async functions, so I don't think their explanations apply here (and if they do, I don't understand how).
If instead of throw we use reject, the above code works fine. I'd like to understand, fundamentally, why throw doesn't work here. Thanks!
This is the async/await version of the Promise constructor antipattern!
Never ever use an async function as a Promise executor function (even when you can make it work1)!
[1: by calling resolve and reject instead of using return and throw statements]
by "asynchronous" they're not referring to async functions, so I don't think their explanations apply here
They could as well. A simple example where it cannot work is
new Promise(async function() {
await delay(…);
throw new Error(…);
})
which is equivalent to
new Promise(function() {
return delay(…).then(function() {
throw new Error(…);
});
})
where it's clear now that the throw is inside an asynchronous callback.
The Promise constructor can only catch synchronous exceptions, and an async function never throws - it always returns a promise (which might get rejected though). And that return value is ignored, as the promise is waiting for resolve to be called.
because the only way to "communicate" to the outside world from within a Promise executor is to use the resolve and reject functions. You could use the following for your example:
function fn() {
return new Promise(async (resolve, reject) => {
// there is no real reason to use an async executor here since there is nothing async happening
try {
throw new Error('<<fn error>>')
} catch(error) {
return reject(error);
}
});
}
An example would be when you want to do something that has convenient async functions, but also requires a callback. The following contrived example copies a file by reading it using the async fs.promises.readFile function with the callback based fs.writeFile function. In the real world, you would never mix fs functions like this because there is no need to. But some libraries like stylus and pug use callbacks, and I use something like this all the time in those scenarios.
const fs = require('fs');
function copyFile(infilePath, outfilePath) {
return new Promise(async (resolve, reject) => {
try {
// the fs.promises library provides convenient async functions
const data = await fs.promises.readFile(infilePath);
// the fs library also provides methods that use callbacks
// the following line doesn't need a return statement, because there is nothing to return the value to
// but IMO it is useful to signal intent that the function has completed (especially in more complex functions)
return fs.writeFile(outfilePath, data, (error) => {
// note that if there is an error we call the reject function
// so whether an error is thrown in the promise executor, or the callback the reject function will be called
// so from the outside, copyFile appears to be a perfectly normal async function
return (error) ? reject(error) : resolve();
});
} catch(error) {
// this will only catch errors from the main body of the promise executor (ie. the fs.promises.readFile statement
// it will not catch any errors from the callback to the fs.writeFile statement
return reject(error);
// the return statement is not necessary, but IMO communicates the intent that the function is completed
}
}
}
Apparently everyone says this is an anti-pattern, but I use it all the time when I want to do some async stuff before doing something that can only be done with a callback (not for copying files like my contrived example). I don't understand why people think it is an anti-pattern (to use an async promise executor), and haven't seen an example yet that has convinced me that it should be accepted as a general rule.

How to throw an exception in the Promise catch method in JavaScript? [duplicate]

Why can't I just throw an Error inside the catch callback and let the process handle the error as if it were in any other scope?
If I don't do console.log(err) nothing gets printed out and I know nothing about what happened. The process just ends...
Example:
function do1() {
return new Promise(function(resolve, reject) {
throw new Error('do1');
setTimeout(resolve, 1000)
});
}
function do2() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject(new Error('do2'));
}, 1000)
});
}
do1().then(do2).catch(function(err) {
//console.log(err.stack); // This is the only way to see the stack
throw err; // This does nothing
});
If callbacks get executed in the main thread, why does the Error get swallowed by a black hole?
As others have explained, the "black hole" is because throwing inside a .catch continues the chain with a rejected promise, and you have no more catches, leading to an unterminated chain, which swallows errors (bad!)
Add one more catch to see what's happening:
do1().then(do2).catch(function(err) {
//console.log(err.stack); // This is the only way to see the stack
throw err; // Where does this go?
}).catch(function(err) {
console.log(err.stack); // It goes here!
});
A catch in the middle of a chain is useful when you want the chain to proceed in spite of a failed step, but a re-throw is useful to continue failing after doing things like logging of information or cleanup steps, perhaps even altering which error is thrown.
Trick
To make the error show up as an error in the web console, as you originally intended, I use this trick:
.catch(function(err) { setTimeout(function() { throw err; }); });
Even the line numbers survive, so the link in web console takes me straight to the file and line where the (original) error happened.
Why it works
Any exception in a function called as a promise fulfillment or rejection handler gets automatically converted to a rejection of the promise you're supposed to return. The promise code that calls your function takes care of this.
A function called by setTimeout on the other hand, always runs from JavaScript stable state, i.e. it runs in a new cycle in the JavaScript event loop. Exceptions there aren't caught by anything, and make it to the web console. Since err holds all the information about the error, including the original stack, file and line number, it still gets reported correctly.
Important things to understand here
Both the then and catch functions return new promise objects.
Either throwing or explicitly rejecting, will move the current promise to the rejected state.
Since then and catch return new promise objects, they can be chained.
If you throw or reject inside a promise handler (then or catch), it will be handled in the next rejection handler down the chaining path.
As mentioned by jfriend00, the then and catch handlers are not executed synchronously. When a handler throws, it will come to an end immediately. So, the stack will be unwound and the exception would be lost. That is why throwing an exception rejects the current promise.
In your case, you are rejecting inside do1 by throwing an Error object. Now, the current promise will be in rejected state and the control will be transferred to the next handler, which is then in our case.
Since the then handler doesn't have a rejection handler, the do2 will not be executed at all. You can confirm this by using console.log inside it. Since the current promise doesn't have a rejection handler, it will also be rejected with the rejection value from the previous promise and the control will be transferred to the next handler which is catch.
As catch is a rejection handler, when you do console.log(err.stack); inside it, you are able to see the error stack trace. Now, you are throwing an Error object from it so the promise returned by catch will also be in rejected state.
Since you have not attached any rejection handler to the catch, you are not able to observe the rejection.
You can split the chain and understand this better, like this
var promise = do1().then(do2);
var promise1 = promise.catch(function (err) {
console.log("Promise", promise);
throw err;
});
promise1.catch(function (err) {
console.log("Promise1", promise1);
});
The output you will get will be something like
Promise Promise { <rejected> [Error: do1] }
Promise1 Promise { <rejected> [Error: do1] }
Inside the catch handler 1, you are getting the value of promise object as rejected.
Same way, the promise returned by the catch handler 1, is also rejected with the same error with which the promise was rejected and we are observing it in the second catch handler.
I tried the setTimeout() method detailed above...
.catch(function(err) { setTimeout(function() { throw err; }); });
Annoyingly, I found this to be completely untestable. Because it's throwing an asynchronous error, you can't wrap it inside a try/catch statement, because the catch will have stopped listening by the time error is thrown.
I reverted to just using a listener which worked perfectly and, because it's how JavaScript is meant to be used, was highly testable.
return new Promise((resolve, reject) => {
reject("err");
}).catch(err => {
this.emit("uncaughtException", err);
/* Throw so the promise is still rejected for testing */
throw err;
});
According the spec (see 3.III.d):
d. If calling then throws an exception e,
a. If resolvePromise or rejectPromise have been called, ignore it.
b. Otherwise, reject promise with e as the reason.
That means that if you throw exception in then function, it will be caught and your promise will be rejected. catch don't make a sense here, it is just shortcut to .then(null, function() {})
I guess you want to log unhandled rejections in your code. Most promises libraries fires a unhandledRejection for it. Here is relevant gist with discussion about it.
Yes promises swallow errors, and you can only catch them with .catch, as explained more in detail in other answers. If you are in Node.js and want to reproduce the normal throw behaviour, printing stack trace to console and exit process, you can do
...
throw new Error('My error message');
})
.catch(function (err) {
console.error(err.stack);
process.exit(0);
});
I know this is a bit late, but I came across this thread, and none of the solutions were easy to implement for me, so I came up with my own:
I added a little helper function which returns a promise, like so:
function throw_promise_error (error) {
return new Promise(function (resolve, reject){
reject(error)
})
}
Then, if I have a specific place in any of my promise chain where I want to throw an error (and reject the promise), I simply return from the above function with my constructed error, like so:
}).then(function (input) {
if (input === null) {
let err = {code: 400, reason: 'input provided is null'}
return throw_promise_error(err)
} else {
return noterrorpromise...
}
}).then(...).catch(function (error) {
res.status(error.code).send(error.reason);
})
This way I am in control of throwing extra errors from inside the promise-chain. If you want to also handle 'normal' promise errors, you would expand your catch to treat the 'self-thrown' errors separately.
Hope this helps, it is my first stackoverflow answer!
Listen for unhandled errors:
window.addEventListener('unhandledrejection', e => {
// ...
});
window.addEventListener('error', e => {
// ...
});
If error gets swallowed, use self.report(error):
.catch(error => {
self.reportError(error);
});
https://developer.mozilla.org/en-US/docs/Web/API/reportError

Why add `async` to a promise callback [duplicate]

I've been trying to get a conceptual understanding of why the following code doesn't catch the throw. If you remove the async keyword from the new Promise(async (resolve, ... part then it works fine, so it has to do with the fact that the Promise executor is an async function.
(async function() {
try {
await fn();
} catch(e) {
console.log("CAUGHT fn error -->",e)
}
})();
function fn() {
return new Promise(async (resolve, reject) => {
// ...
throw new Error("<<fn error>>");
// ...
});
}
The answers here, here, and here repeat that "if you're in any other asynchronous callback, you must use reject", but by "asynchronous" they're not referring to async functions, so I don't think their explanations apply here (and if they do, I don't understand how).
If instead of throw we use reject, the above code works fine. I'd like to understand, fundamentally, why throw doesn't work here. Thanks!
This is the async/await version of the Promise constructor antipattern!
Never ever use an async function as a Promise executor function (even when you can make it work1)!
[1: by calling resolve and reject instead of using return and throw statements]
by "asynchronous" they're not referring to async functions, so I don't think their explanations apply here
They could as well. A simple example where it cannot work is
new Promise(async function() {
await delay(…);
throw new Error(…);
})
which is equivalent to
new Promise(function() {
return delay(…).then(function() {
throw new Error(…);
});
})
where it's clear now that the throw is inside an asynchronous callback.
The Promise constructor can only catch synchronous exceptions, and an async function never throws - it always returns a promise (which might get rejected though). And that return value is ignored, as the promise is waiting for resolve to be called.
because the only way to "communicate" to the outside world from within a Promise executor is to use the resolve and reject functions. You could use the following for your example:
function fn() {
return new Promise(async (resolve, reject) => {
// there is no real reason to use an async executor here since there is nothing async happening
try {
throw new Error('<<fn error>>')
} catch(error) {
return reject(error);
}
});
}
An example would be when you want to do something that has convenient async functions, but also requires a callback. The following contrived example copies a file by reading it using the async fs.promises.readFile function with the callback based fs.writeFile function. In the real world, you would never mix fs functions like this because there is no need to. But some libraries like stylus and pug use callbacks, and I use something like this all the time in those scenarios.
const fs = require('fs');
function copyFile(infilePath, outfilePath) {
return new Promise(async (resolve, reject) => {
try {
// the fs.promises library provides convenient async functions
const data = await fs.promises.readFile(infilePath);
// the fs library also provides methods that use callbacks
// the following line doesn't need a return statement, because there is nothing to return the value to
// but IMO it is useful to signal intent that the function has completed (especially in more complex functions)
return fs.writeFile(outfilePath, data, (error) => {
// note that if there is an error we call the reject function
// so whether an error is thrown in the promise executor, or the callback the reject function will be called
// so from the outside, copyFile appears to be a perfectly normal async function
return (error) ? reject(error) : resolve();
});
} catch(error) {
// this will only catch errors from the main body of the promise executor (ie. the fs.promises.readFile statement
// it will not catch any errors from the callback to the fs.writeFile statement
return reject(error);
// the return statement is not necessary, but IMO communicates the intent that the function is completed
}
}
}
Apparently everyone says this is an anti-pattern, but I use it all the time when I want to do some async stuff before doing something that can only be done with a callback (not for copying files like my contrived example). I don't understand why people think it is an anti-pattern (to use an async promise executor), and haven't seen an example yet that has convinced me that it should be accepted as a general rule.

Categories

Resources