Should I refrain from handling Promise rejection asynchronously?
Note: See 2020 updates below for changes in Node v15
"Should I refrain from handling Promise rejection asynchronously?"
Those warnings serve an important purpose but to see how it all works see those examples:
Try this:
process.on('unhandledRejection', () => {});
process.on('rejectionHandled', () => {});
var prm = Promise.reject(new Error('fail'));
setTimeout(() => {
prm.catch((err) => {
console.log(err.message);
})
}, 0);
Or this:
var prm = Promise.reject(new Error('fail'));
prm.catch(() => {});
setTimeout(() => {
prm.catch((err) => {
console.log(err.message);
})
}, 0);
Or this:
var caught = require('caught');
var prm = caught(Promise.reject(new Error('fail')));
setTimeout(() => {
prm.catch((err) => {
console.log(err.message);
})
}, 0);
Disclaimer: I am the author of the caught module (and yes, I wrote it for this answer).
Rationale
It was added to Node as one of the Breaking changes between v6 and v7. There was a heated discussion about it in Issue #830: Default Unhandled Rejection Detection Behavior with no universal agreement on how promises with rejection handlers attached asynchronously should behave - work without warnings, work with warnings or be forbidden to use at all by terminating the program. More discussion took place in several issues of the unhandled-rejections-spec project.
This warning is to help you find situations where you forgot to handle the rejection but sometimes you may want to avoid it. For example you may want to make a bunch of requests and store the resulting promises in an array, only to handle it later in some other part of your program.
One of the advantages of promises over callbacks is that you can separate the place where you create the promise from the place (or places) where you attach the handlers. Those warnings make it more difficult to do but you can either handle the events (my first example) or attach a dummy catch handler wherever you create a promise that you don't want to handle right away (second example). Or you can have a module do it for you (third example).
Avoiding warnings
Attaching an empty handler doesn't change the way how the stored promise works in any way if you do it in two steps:
var prm1 = Promise.reject(new Error('fail'));
prm1.catch(() => {});
This will not be the same, though:
var prm2 = Promise.reject(new Error('fail')).catch(() => {});
Here prm2
will be a different promise then prm1
. While prm1
will be rejected with 'fail' error, prm2
will be resolved with undefined
which is probably not what you want.
But you could write a simple function to make it work like a two-step example above, like I did with the caught
module:
var prm3 = caught(Promise.reject(new Error('fail')));
Here prm3
is the same as prm1
.
See: https://www.npmjs.com/package/caught
2017 Update
See also Pull Request #6375: lib,src: "throw" on unhandled promise rejections (not merged yet as of Febryary 2017) that is marked as Milestone 8.0.0:
Makes Promises "throw" rejections which exit like regular uncaught errors. [emphasis added]
This means that we can expect Node 8.x to change the warning that this question is about into an error that crashes and terminates the process and we should take it into account while writing our programs today to avoid surprises in the future.
See also the Node.js 8.0.0 Tracking Issue #10117.
2020 Update
See also Pull Request #33021: process: Change default --unhandled-rejections=throw (already merged and released as part of the v15 release - see: release notes) that once again makes it an exception:
As of Node.js 15, the default mode for
unhandledRejection
is changed tothrow
(fromwarn
). Inthrow
mode, if anunhandledRejection
hook is not set, theunhandledRejection
is raised as an uncaught exception. Users that have anunhandledRejection
hook should see no change in behavior, and it’s still possible to switch modes using the--unhandled-rejections=mode
process flag.
This means that Node 15.x has finally changed the warning that this question is about into an error so as I said in 2017 above, we should definitely take it into account while writing our programs because if we don't then it will definitely cause problems when upgrading the runtime to Node 15.x or higher.
Receiving `UnhandledPromiseRejectionWarning` even though rejected promise is handled
What am I overlooking?
Your yieldedOutPromise.value.then
call is creating a new promise, and if yieldedOutPromise.value
rejects then it will be rejected as well. It doesn't matter that you handle the error via .catch
on yieldedOutPromise.value
, there's still a rejected promise around, and it's the one that will be reported.
You are basically splitting your promise chain, which leads to each end needing an error handler. However, you shouldn't be splitting anything at all. Instead of the
promise.then(onSuccess);
promise.catch(onError);
antipattern you should use
promise.then(onSuccess, onError).…
Oh, and while you're at it, avoid the Promise
constructor antipattern. Just do
module.exports = function runGen (generatorFunc, startValue) {
return Promise.resolve(startValue).then(generatorFunc).then(generator => {
return keepIterating({done: false, value: undefined});
function keepIterating({done, value}) {
if (done) return value;
return Promise.resolve(value).then(fulfilledValue =>
generator.next(fulfilledValue)
, err =>
generator.throw(err)
).then(keepIterating);
}
});
};
How to throw exception from async function
Ultimately, what you're trying to do is calling an asynchronous function f
in a synchronous function g
, which won't work (that would be the equivalent of being able to turn an asynchronous function into a synchronous function).
Instead, g
either has to be async
as well, or it should properly handle the promise returned by f
. However, in this comment you state that the function represented by f
may not always return a promise, in which case the former option would be the easiest to implement:
async g(obj, handler) {
return await handler(obj);
}
This will also work if handler
doesn't return a promise, but just a value (which is documented here).
Calling g
(again) requires either an async function, or code to handle its returned promise:
g(objToSave, f).then(...).catch(...)
Bluebird.js Unhandled Rejection Error using request
Instead of this:
grabSomeData.then((fulfilled, rejected) => {
console.log('res: ' + fulfilled);
console.log('rej: ' + rejected);
});
You need to use:
grabSomeData.then((fulfilled) => {
console.log('res: ' + fulfilled);
}, (rejected) => {
console.log('rej: ' + rejected);
});
Or:
grabSomeData.then((fulfilled) => {
console.log('res: ' + fulfilled);
}).catch((rejected) => {
console.log('rej: ' + rejected);
});
For more info on the unhandled rejection warnings (that will be fatal errors in the future) see this answer:
- Should I refrain from handling Promise rejection asynchronously?
Using the docs but completely confused... mongoose Model#save
Your problem is two fold, and both of the errors you are getting are telling you exactly what is wrong. The source of your troubles is lack of understanding (not trying to pick on you). This is kind of the same as if you were a beginner at electronics and I told your that a tube is biased improperly and you said "I don't understand" - well, you need to learn about tubes then because I just described to you the exact problem with your circuit.
1. Promise error - UnhandledPromiseRejectionWarning: Unhandled promise rejection...
. This cannot be more descript. Promises are either 1) resolved or 2) rejected. If you fail to "handle" the rejected case, you will get an error. Handling a rejected promise can happen one of two ways:
// pass a 2nd function to `.then()`:
somePromise.then(function (result) {
// promise was "resolved" successfully
}, function (err) {
// Promise was "rejected"
});
// use `.catch()` - this is preferred in my opinion:
somePromise.then(function (result) {
// promise was "resolved" successfully
}).catch(function (err) {
// Promise was "rejected"
});
Using either of the above scenarios means you "handled" the promise rejection correctly.
2. Database validation - Path 'name' is required.
. This literally means that you have a required path called "name" which is required (meaning, it must have a value). So looking at your code it's pretty obvious:
const skill = new Skill({});
skill.save() //-> results in error
This is fixed by adding all required data before saving:
const skill = new Skill({ name: "Jason" });
skill.save() //-> yay, no error
Related Topics
Replace All Spaces in a String with '+'
Capture Click on Div Surrounding an Iframe
Pagination on a List Using Ng-Repeat
What Are Alternatives to Extjs
Selecting Multiple Classes with Jquery
How to Persist a Es6 Map in Localstorage (Or Elsewhere)
Js Strings "+" VS Concat Method
Angularjs $Resource Restful Example
Es6 Class Multiple Inheritance
Angular2 Component's "This" Is Undefined When Executing Callback Function
How to Resolve Typeerror: Cannot Convert Undefined or Null to Object
Unsigned Integer in JavaScript
Jquery: How to Get the Event Object in an Event Handler Function Without Passing It as an Argument