throw Error after promise is rejected - Q - javascript

following is a short example for using a promise with Q.
this is test1.js:
function testDefer() {
var deferred = Q.defer();
fs.readFile("foo.txt", "utf-8", function (error, text) {
if (error) {
deferred.reject(new Error(error));
} else {
deferred.resolve(text);
}
});
return deferred.promise;
}
and this is test2.js
(function(){
'use strict';
var test1 = require('./test1');
test1.testDefer().then(
function(data){
console.log('all good');
},
function(err) {
//upon error i might want to throw an exception, however, it is not thrown / ignored.
throw new Error('I want to throw this exception');
}
);
})();
i want to throw an exception in test2 in case the promise is rejected (or in some cases when it's resolved). anyway, the exception is ignored and the program finishes without throwing the exception.
my question is, how to throw errors from the success/failure functions?
thank you

All errors in then handlers are caught and used to reject the resulting promise. What you want is the done method:
Much like then, but with different behavior around unhandled
rejection. If there is an unhandled rejection, either because
promise is rejected and no onRejected callback was provided, or
because onFulfilled or onRejected threw an error or returned a
rejected promise, the resulting rejection reason is thrown as an
exception in a future turn of the event loop.
This method should be used to terminate chains of promises that will
not be passed elsewhere. Since exceptions thrown in then callbacks
are consumed and transformed into rejections, exceptions at the end of
the chain are easy to accidentally, silently ignore. By arranging for
the exception to be thrown in a future turn of the event loop, so that
it won't be caught, it causes an onerror event on the browser
window, or an uncaughtException event on Node.js's process
object.
The Golden Rule of done vs. then usage is: either return your
promise to someone else, or if the chain ends with you, call done to
terminate it.
Q.ninvoke(fs, "readfile", "foo.txt", "utf-8").done(function(data){
console.log('all good');
}, function(err) {
throw new Error('I want to throw this exception');
}); // or omit the error handler, and 'err' will be thrown

Related

If a promise is fine to be rejected, then how should we handle it or just not even handle it or catch it?

If there are situations where it is fine for a promise to be rejected, due to this rejection, an error is still thrown. How should we handle it or just ignore it?
Case 1: If the user can click a cancel button and an operation is canceled and we consider such cancelation is fine, do we just handle it by:
p.then(value => {
// fulfillment handler
}, err => {}); // do nothing
or
p.then((value) => {
// fulfillment handler
}).catch(err => {}); // do nothing
Or can we just totally ignore it and not use a rejection handler or catch()?
Case 2: What if we do a
let p = Promise.reject(1);
then an exception is immediately thrown. There is no time to even catch it. Then must we always use:
let p = Promise.reject(1).catch(err => {});
? Actually, if p above is printed, it is a resolved promise, while the one without the catch() is a rejected promise, so I am a bit confused. I thought both should be in a rejected state.
If you use a promise rejection/exception to signal that something was cancelled, it makes sense to catch this error.
But, an important rule of thumb is that if you catch an error and you expect a certain error to be thrown (CancellationError for example), you should make sure in your catch clause that it's specifically an error of that type.
Otherwise you run the risk of some other exception being thrown and silently eaten up by your (empty) catch handler.
So I would usually expect this to look something like this:
try {
await someOperation();
} catch (err) {
if (!(err instanceof Cancellation)) {
// Re-throw
throw err;
}
console.log('Operation cancelled');
}

What is actually happening when you omit .catch from JavaScript Promises?

The following two functions behave identically when called.
function func1() {
return asyncFunc()
.then(() => {
// do something
});
}
function func2() {
return asyncFunc()
.then(() => {
// do something
}).catch((err) => {
throw err;
});
}
I know that .catch() is merely syntactic sugar for Promise.prototype.then(undefined, onRejected) per MDN docs. However, I'm confused as to what actually occurs behind the scenes when you omit .catch from a promise chain.
What is actually going on behind the scenes when there is no .catch() in a promise chain? Is a .catch((err) => { throw err;}); being "magically" appended somehow?
The .catch((err) => { throw err;}) would not do anything, it would just re throw the error. So the Promise returned by .catch will be rejected with the error err again:
Promise.reject(new Error('test'))
.catch(err => {
console.error(err)
throw err
})
.catch(err => {
console.error(err)
})
func1 would return a Promise that might be rejected by some event that happens in that chain. So the caller of the func1 might want to handle that error, or if the the caller passes the received Promise further, and does not want to handle that error, then also the caller can omit the catch.
But the one "owning" the chain (the one who received it last and does not pass it to anyone else), is responsible to handle the rejection case.
function func1() {
return asyncFunc()
.then(() => {
// do something
});
}
function callerA() {
return func1()
}
function callerB() {
// callerB does not return the promise retuirned by callerA,
// and does not pass it to any other function, so it has to handle
// the rjection case
callerA().catch(err => {
})
}
On a rejection the js environment will check if there this error is catched anywhere in the chain. If that is not the case then a UnhandledPromiseRejectionWarning might be thrown:
UnhandledPromiseRejectionWarning: Unhandled promise rejection. 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(). (rejection id: 1)
Or in case of the browser the error is logged in the console.
How the js enviroment deals with unhandled rejections depends, nodejs currently emits this warning:
DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
And as it says will terminate the application in future versions.
For the browser it is unlikely that it will terminate the tab future. It most likely will always only log that error in the console, but you still should write your code as if the unhandled rejection would terminate the context.
The two examples are definitely functionally equivalent, there is no magic happening behind the scenes. The Promise handler callbacks are designed to automatically handle values that are thrown and apply them to the promise chain.
Consider the following example:
Promise.resolve("{") // broken JSON
.then((json) => {
return JSON.parse(json) // Throws SyntaxError
})
.catch((err) => {
throw err; // Throws the same SyntaxError
})
.catch((err) => {
console.error(err); // Logs the SyntaxError
});
Most Promise implementations have a feature that logs the error when you have not attached an error handler to a promise that was rejected, some even allow you to register a custom event handler (e.g. Chrome and NodeJS) when an unhandled rejection occurs. But not all runtimes are created equal, the Promise implementation in Edge, for instance, does not provide such feature for notifying users of unhandled rejections.
Q: Is a .catch((err) => { throw err;}); being "magically" appended somehow?
A: NO, NOT AT ALL!
The .then method itself returns a promise. And, if you call a .catch method on that returned promise the handler passed in the catch would get called if that promise got rejected. The handler would simply get ignored if the promise got resolved. Read more about error handling in JS promises here.
And, as I already mentioned, the catch method doesn't magically get appended. Your code would just get ignore if the promise returned by .then method would get rejected because you didn't catch it. Run the following code for a better understanding:
var myPromise = new Promise(function(resolve, reject) {
setTimeout(reject, 100);
});
myPromise
.then(() => console.log('hi')); // Prints nothing
myPromise
.then(() => console.log('hi'))
.catch(() => console.log('hello')); // Prints hello

Error in Promise() constructor not handled by .catch() method

When I run the following in Chrome, I get an "Uncaught (in promise) failure" error message:
var p = new Promise(function(resolve){
throw "failure";
})
p.then(function(){
console.log("success");
})
p.catch(function(err){
console.log(err);
});
I don't get an error if I run:
var p = new Promise(function(resolve){
throw "failure";
})
p.then(function(){
console.log("success");
}).catch(function(err){
console.log(err);
});
or this:
var p = new Promise(function(resolve){
throw "failure";
}).catch(function(err){
console.log(err);
});
What's going on here? I'm relatively new to Promises, so I'm probably missing something simple...
The first case didn't work for you because you tried to catch the error from the Promise directly where you was using in the same time .then, in this case you catch the error from the .then. In the other hand, where there is no .then you can catch directly from the Promise object.
.catch is a shorter alias for .then. The catch() method returns a Promise object and only deals with cases where the initial promise is rejected. It has the same effect as a call to Promise.prototype.then(undefined, ifRejected) (this is actually what happens in the engine, obj.catch(onRejected) is translated into obj.then(undefined, onRejected)) . So you can not put Promise.catch().
Lesson here. If you chain your .then() and .catch() handlers directly to the promise (without creating an intermediate p variable and intermediate promise chains) and you have a .catch() handler at the end of every chain, you are very unlikely (perhaps completely unlikely) to get the "unhandled promise rejection" message.
Based on other occurrences of this I've seen both in my own code and here on stack overflow, it seems to me that the interpreter or promise library is somewhat guessing when the unhandled rejection situation occurs and is not perfect at its guessing.
In your case, the interpreter saw that p.then() does not have a .catch() handler and then saw p get rejected. So, that promise chain does not actually have a .catch() handler. So, that promise chain will end with a rejection and there was no handler. That triggers the warning.
It wasn't smart enough to see that there was a separate p.catch() or didn't think that justified not showing the warning.
As you've already discovered, this will always be safe:
new Promise(function(resolve){
throw "failure";
}).then(function(data) {
console.log(data);
}).catch(function(err){
console.log(err);
});
because there are no promise chains that can ever get a rejection that isn't caught by a .catch(). Think twice about saving a promise to a variable at all - just make a single promise chain (or branched chains inside of .then() handlers if needed). I've almost never found it required to save a promise into a variable except occasionally when I'm returning it from a function and executing some code in the function after the promise was created. Usually, you can just do:
f().then(...).then(...).catch(...);

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

Promise reject() causes "Uncaught (in promise)" warning

Once a promise reject() callback is called, a warning message "Uncaught (in promise)" appears in the Chrome console. I can't wrap my head around the reason behind it, nor how to get rid of it.
var p = new Promise((resolve, reject) => {
setTimeout(() => {
var isItFulfilled = false
isItFulfilled ? resolve('!Resolved') : reject('!Rejected')
}, 1000)
})
p.then(result => console.log(result))
p.catch(error => console.log(error))
Warning:
Edit:
I found out that if the onRejected handler is not explicitly provided to the .then(onResolved, onRejected) method, JS will automatically provide an implicit one. It looks like this: (err) => throw err. The auto generated handler will throw in its turn.
Reference:
If IsCallable(onRejected)` is false, then
Let onRejected be "Thrower".
http://www.ecma-international.org/ecma-262/6.0/index.html#sec-performpromisethen
This happens because you do not attach a catch handler to the promise returned by the first then method, which therefore is without handler for when the promise rejects. You do have one for the promise p in the last line, but not for the chained promise, returned by the then method, in the line before it.
As you correctly added in comments below, when a catch handler is not provided (or it's not a function), the default one will throw the error. Within a promise chain this error can be caught down the line with a catch method callback, but if none is there, the JavaScript engine will deal with the error like with any other uncaught error, and apply the default handler in such circumstances, which results in the output you see in the console.
To avoid this, chain the .catch method to the promise returned by the first then, like this:
p.then( result => console.log('Fulfilled'))
.catch( error => console.log(error) );
Even if you use Promises correctly: p.then(p1).catch(p2) you can still get an uncaught exception if your p2 function eventually throws an exception which you intend to catch using a mechanism like window.onerror. The reason is that the stack has already been unwound by the error handling done in the promise. To fix this, make sure that your error code (called by the reject function) does not throw an exception. It should simply return.
It would be nice if the error handling code could detect that the stack has already been unwound (so your error call doesn't have to have a flag for this case), and if anyone knows how to do this easily I will edit this answer to include that explanation.
This code does not cause the "uncaught in promise" exception:
// Called from top level code;
// implicitly returns a Promise
testRejectCatch = async function() {
// Nested within testRejectCatch;
// simply rejects immediately
let testReject = function() {
return new Promise(function(resolve, reject) {
reject('test the reject');
)};
}
//***********************************************
// testRejectCatch entry.
//***********************************************
try {
await testReject(); // implicitly throws reject exception
catch(error) {
// somecode
}
//***********************************************
// top level code
//***********************************************
try{
testRejectCatch() // Promise implicitly returned,
.catch((error) => { // so we can catch
window.alert('Report error: ' + error);
// must not throw error;
});
}
catch(error) {
// some code
}
Explanation:
First, there's a terminology problem. The term "catch" is
used in two ways: in the try-catches, and in the Promises.
So, it's easy to get confused about a "throw"; is it throwing
to a try's catch or to a Promise's catch?
Answer: the reject in testReject is throwing to the Promise's
implicit catch, at await testReject; and then throwing on to
the .catch at testRejectCatch().
In this context, try-catch is irrelevant and ignored;
the throws have nothing to do with them.
The .catch at testRejectCatch satisfies the requirement
that the original throw must be caught somewhere,
so you do not suffer the "uncaught in Promise..." exception.
The takeaway: throws from Promises are throws to .catch,
not to try-catch; and must be dealt-with in some .catch
Edit:
In the above code, the reject propagates up through the .catches.
If you want, you can convert over to propagating up the try-catches.
At line 17, change the code to:
let bad = '';
await testReject().catch((error) => {bad = error});
if (bad) throw bad;
Now, you've switched over to the try-catch.
I ran into this issue, but without setTimeout().
In case anyone else runs into this: if in the Promise constructor you just call reject() synchronously, then it doesn't matter how many .then() and .catch() handlers you add to the returned Promise, they won't prevent an uncaught promise rejection, because the promise rejection would happen before you
I've solved that problem in my project, it's a large enterprise one. My team is too lazy to write empty catch hundreds of times.
Promise.prototype.then = function (onFulfilled, onRejected) {
return baseThen.call(this, (x: any) => {
if (onFulfilled)
onFulfilled(x);
}, (x: any) => {
if (onRejected)
onRejected(x);
});
};

Categories

Resources