I'm trying to wrap my head around this issue I'm facing concerning async/await and Promises. I managed to boil my issue down to the following code:
async function sleep(ms: number) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function fetchMock(): Promise<any> {
return new Promise(() => {
throw 'error fetching result';
});
}
async function main(): Promise<any> {
const kickedOffRequest = fetchMock();
await sleep(10);
return kickedOffRequest;
}
main()
.then(() => console.log('resolved promise!'))
.catch(error => console.error('caught error!', error));
I receive the following warning:
(node:82245) UnhandledPromiseRejectionWarning: error fetching result
(node:82245) 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)
(node:82245) [DEP0018] 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.
caught error! error fetching result
(node:82245) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
You can observe the same issue in this sandbox. I noticed that commenting out the await sleep(10) fixes the issue, but I apparently know less about promises than I thought. Why does commenting that line out make my program work? I'm tempted to ask how to fix the Promise rejection was handled asynchronously error, but I hope that once I understand how await sleep(10) causes the error I get I will be able to fix this one on my own.
Thanks in advance for taking the time to read/answer this question!
The original concept of promises was that you could have a rejected promise sitting around for some time before attaching a catch handler to it. For example, Firefox used to warn of uncaught rejection errors only when a rejected promise with no rejection handler was garbage collected from memory.
Somebody decided that programmers couldn't be trusted with managing promise rejections properly and changed the HTML spec to require browsers to throw "unhandled promise rejection" errors if a rejected promise has no rejection handlers added before code returns to the event loop.
(I think unhandled rejections can survive without error in the micro task queue for a tick or two, before control returns to the event loop proper, but haven't tested it lately.)
The ECMAScript specification added an abstract means of notifying the host environment of an unhandled promise rejection without specifying what, if any, action should be taken.
On a case by case basis you can prevent the host being notified by adding a rejection handler that is never used. The reasoning is that adding a dummy rejection handler to a promise means that should it be rejected it has a rejection handler already - or if it was rejected the host is notified the promise now has a rejection handler - and you can call then and catch multiple times on the same promise.
Changing
async function fetchMock(){
return new Promise(() => {
throw 'error fetching result';
});
}
to
async function fetchMock(){
let promise = new Promise(() => {
throw 'error fetching result';
});
promise.catch(()=>null); // unused rejection handler
return promise;
}
should work around the unwanted HTML5 host behavior implemented in V8, the JavaScript engine used in node.
The detection of unhandled rejection in node.js is imperfect. There are specific spots in the life cycle of a rejected promise where the engine checks to see if there's a handler and it does not always wait until the last possible moment so it can miss places that you add a handler. In your specific case, you may need to attach a .catch() handler locally, then finish up the work you want to do, then rethrow the error. This work-around will work for you while still maintaining the desired resolve/reject from main() (e.g. without changing the interface to main).
So, this isn't particularly super pretty, but it meets the spec we talked about in comments.
main() calls fetchMock()
If it resolves or rejects quickly (before some custom delay time), then it holds off on the resolve or the reject until at least that delay time has elapsed from when fetchMock() was originally called.
If fetchMock() takes longer than that custom delay time to resolve or reject, then no further delay is added.
The promise that main() returns then follows the promise that fetchMock() returned, either rejected or resolved with the same reason or value.
The key ingredient is that it captures the time right before calling fetchMock() and then when fetchMock() either resolves or rejects, it decides whether to delay any more time before passing the resolve/reject value/reason on through.
function sleep(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
function fetchMock() {
return new Promise((resolve) => {
throw 'error fetching result';
//resolve('this is our result');
});
}
function handler(start, minWaitTime, isErr = false) {
return async function(val) {
let diff = minWaitTime - (Date.now() - start);
if (diff > 0) {
await sleep(diff);
}
if (isErr) {
throw val;
} else {
return val;
}
}
}
function main() {
let start = Date.now();
const minWaitTime = 1000;
return fetchMock().then(handler(start, minWaitTime), handler(start, minWaitTime, true));
}
main()
.then(() => console.log('resolved promise!'))
.catch(error => console.error('caught error!', error));
Note, also that sleep() and fetchMock() already directly return promises and don't use await so there is no requirement for them to be async.
The problem is that the fetchMock rejects immediately, and when a Promise rejects, at the time that it rejects, it must be chained with a .catch somewhere in order to prevent an Unhandled Promise Rejection.
With your await sleep(10), the kickedOffRequest promise rejects while the main function is still waiting for sleep to resolve. When there's a rejection, the interpreter doesn't look ahead to see if the Promise may be caught in the future (for example, to see if the Promise gets returned or caught) - the Promise must be caught now.
When you remove the await, kickedOffRequest still becomes a rejected Promise, but it's returned from main immediately, so at the point that the Promise rejects, it can be seen and caught by the outer .catch, so there's no Unhandled Rejection warning.
It's good if a Promise rejection can be handled right when it rejects, but if that's not an option for you, you can put another .catch onto the end of the inner Promise. That way, the warning won't appear (because of the existence of .catch, even if that .catch didn't do anything meaningful) and you can later check to see if an error actually occurred or not:
async function sleep(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function fetchMock(){
return new Promise(() => {
throw 'error fetching result';
});
}
async function main() {
const kickedOffRequest = fetchMock()
.then(resolved => ({ resolved }))
.catch(rejected => ({ rejected }));
await sleep(10);
return kickedOffRequest;
}
main()
.then(({ resolved, rejected }) => {
if (resolved) {
console.log('resolved promise!');
} else {
console.error('caught error!', rejected);
}
});
This is pretty similar to how Promise.allSettled works (though it's more intended for when there's an array of Promises that need to be parsed):
async function sleep(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
async function fetchMock(){
return new Promise(() => {
throw 'error fetching result';
});
}
async function main() {
const kickedOffRequestArr = Promise.allSettled([fetchMock()]);
await sleep(10);
return kickedOffRequestArr;
}
main()
.then(([{ result, reason, value }]) => {
if (result === 'fulfilled') {
console.log('resolved promise!', value);
} else {
console.error('caught error!', reason);
}
});
Related
Today I was just exploring Promises in JavaScript and I came across this:
Promise.reject("Failed");
Gives
Promise { <state>: "rejected", <reason>: "Failed" }
Uncaught (in promise) Failed
Whereas,
Promise.reject("Failed").catch((reason) => console.log(reason));
Gives
Failed
Promise { <state>: "fulfilled", <value>: undefined }
I get the part that in the latter, the rejection is caught and hence just a normal console message but why is the promise itself changed to fulfilled when it was rejected.
A call to .catch will return a pending promise.
Whether that pending promise will fulfill depends on:
The promise on which it is called. It that promise fulfils, then the catch promise will just follow suit and resolve to the same outcome without ever executing the callback that was passed as argument.
If the promise on which it is called is rejected (like in your example), then the callback (passed to catch as argument) is executed. This execution determines how the catch-returned promise (that was pending) is resolved. For instance, if the callback throws an error, that promise will actually get in a rejected state.
Demo:
let p = Promise.reject();
let q = p.catch(() => {
console.log("p rejected");
// Return a promise that will reject 1 second later
return new Promise((resolve, reject) => setTimeout(reject, 1000));
});
let r = q.catch(() => {
console.log("q rejected!");
});
Every Promise.then and Promise.catch returns a fulfilled promise with the value returned from the function handler given to then or catch clause.
Let's take a look at your code.
Promise.reject("Failed").catch((reason) => console.log(reason));
The first Promise.reject("Failed") returns a rejected promise with the value "Failed". The catch clause has an arrow functions. And what is this arrow function returning you may ask? The value returned by console.log which is undefined.
How do you test it you may ask? Simple, paste this in your console.
Promise.reject("Failed").catch((reason) => {
console.log(reason);
return "Whoa, what is this?";
});
I get this error:
(node:9868) UnhandledPromiseRejectionWarning: #<Object>
(node:9868) 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)
(node:9868) [DEP0018] 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
I am wrapping the create method with express error handler that looks like this:
export const errorHandler = (callback: any) => {
return (req: Request, res: Response, next: NextFunction) => {
callback(req, res, next).catch(next);
};
};
when I try to do this:
async create(entity: T){
this._model
.findOne({ name: (entity as any).name })
.then((res) => {
if (res) {
throw Exceptions.ENTITY_EXISTS; // doesnt work
}
})
}
however when I change it to be async await it works fine
async create(entity: T) {
const res = await this._model.findOne({ name: (entity as any).name });
if (res) throw Exceptions.ENTITY_EXISTS;
}
Also when I tried to do instead of throw Promise.reject(Exceptions.ENTITY_EXISTS) it threw the same error.
Can someone explain me the differences between these three, and also why only the async await is working?
The difference is indeed where the throw happens. In the working version, it makes the promise returned by create() to settle as rejected.
In the non-working version it makes the promise returned by .then() to settle as rejected. However this promise is not the one returned by create(), and so even if you correctly handle a rejection on create()... that doesn't handle the rejection that happened on the then() promise. They are not connected, and so you don't handle that rejection.
To fix the first version, make sure to return the promise returned by .then():
create(entity: T){
return this._model
// ^^^^^^
.findOne({ name: (entity as any).name })
.then((res) => {
if (res) {
throw Exceptions.ENTITY_EXISTS; // doesnt work
}
});
}
NB: Although you could keep async before the function, it really is not so useful if you don't use await and return a promise.
NB2: Because of this missing return, the create function actually returned a promise that resolved immediately... so it was never awaiting the database query.
I am implementing a function called timeout to convert a promise into a promise that will reject if not settled within a minimum amount of time.
With the following implementation, I get an uncaught (in promise) message in Chrome devtools when the promise passed to timeout rejects before the allotted time.
function timeout(promise, duration, message) {
const timeoutPromise = new Promise((_, reject) => {
const handle = setTimeout(() => {
reject(new TimeoutError(message ?? "Operation timed out."))
}, duration)
promise.finally(() => clearTimeout(handle)) //problem line
})
return Promise.race([promise, timeoutPromise])
}
If I change it to the following however, I seem to have no issues.
function timeout(promise, duration, message) {
const timeoutPromise = new Promise((_, reject) => {
const handle = setTimeout(() => {
reject(new TimeoutError(message ?? "Operation timed out."))
}, duration)
const clear = () => clearTimeout(handle)
promise.then(clear).catch(clear)
})
return Promise.race([promise, timeoutPromise])
}
I'm trying to understand how calling then & catch in the second scenario is different to calling finally in the first.
This is because finally handlers are different from then and catch handlers. If the promise is rejected, a finally handler can't change rejection to resolution. Like the finally block of a try/catch/finally structure, it's meant to be largely transparent.
Here's how I describe it in my new book (Chapter 8):
In the normal case, a finally handler has no effect on the fulfillment or rejection passing through
it (like a finally block): any value it returns other than a thenable that is/gets rejected is
ignored. But if it throws an error or returns a thenable that rejects, that error/rejection supersedes
any fulfillment or rejection passing through it (just like throw in a finally block). So it can't change
a fulfillment value — not even if it returns a different value — but it can change a rejection reason into a
different rejection reason, it can change a fulfillment into a rejection, and it can delay a fulfillment (by
returning a thenable that is ultimately fulfilled).
Or to put it another way: it's like a finally block that can't contain a return statement. (It might
have a throw or might call a function or do some operation that throws, but it can't return a new value.
Here's an example of not being able to convert rejection to fulfillment:
Promise.reject(new Error("Failed"))
.finally(() => {
console.log("finally handler returns null");
return null;
});
Look in the browser's real console to see the unhandled rejection error.
Another way to deal with it in your code is to do this in place of the problem line:
promise.catch(() => {}).finally(() => clearTimeout(handle))
or
promise.catch(() => {}).then(() => clearTimeout(handle))
That converts rejection to fulfillment (only on this branch) prior to the finally (or then) handler.
For learning Angular 2, I am trying their tutorial.
I am getting an error like this:
(node:4796) UnhandledPromiseRejectionWarning: Unhandled promise rejection (r ejection id: 1): Error: spawn cmd ENOENT
[1] (node:4796) 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.
I went through different questions and answers in SO but could not find out what an "Unhandled Promise Rejection" is.
Can anyone simply explain me what it is and also what Error: spawn cmd ENOENT is, when it arises and what I have to check to get rid of this warning?
The origin of this error lies in the fact that each and every promise is expected to handle promise rejection i.e. have a .catch(...) . you can avoid the same by adding .catch(...) to a promise in the code as given below.
for example, the function PTest() will either resolve or reject a promise based on the value of a global variable somevar
var somevar = false;
var PTest = function () {
return new Promise(function (resolve, reject) {
if (somevar === true)
resolve();
else
reject();
});
}
var myfunc = PTest();
myfunc.then(function () {
console.log("Promise Resolved");
}).catch(function () {
console.log("Promise Rejected");
});
In some cases, the "unhandled promise rejection" message comes even if we have .catch(..) written for promises. It's all about how you write your code. The following code will generate "unhandled promise rejection" even though we are handling catch.
var somevar = false;
var PTest = function () {
return new Promise(function (resolve, reject) {
if (somevar === true)
resolve();
else
reject();
});
}
var myfunc = PTest();
myfunc.then(function () {
console.log("Promise Resolved");
});
// See the Difference here
myfunc.catch(function () {
console.log("Promise Rejected");
});
The difference is that you don't handle .catch(...) as chain but as separate. For some reason JavaScript engine treats it as promise without un-handled promise rejection.
This is when a Promise is completed with .reject() or an exception was thrown in an async executed code and no .catch() did handle the rejection.
A rejected promise is like an exception that bubbles up towards the application entry point and causes the root error handler to produce that output.
See also
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch
Promises can be "handled" after they are rejected. That is, one can call a promise's reject callback before providing a catch handler. This behavior is a little bothersome to me because one can write...
var promise = new Promise(function(resolve) {
kjjdjf(); // this function does not exist });
... and in this case, the Promise is rejected silently. If one forgets to add a catch handler, code will continue to silently run without errors. This could lead to lingering and hard-to-find bugs.
In the case of Node.js, there is talk of handling these unhandled Promise rejections and reporting the problems. This brings me to ES7 async/await. Consider this example:
async function getReadyForBed() {
let teethPromise = brushTeeth();
let tempPromise = getRoomTemperature();
// Change clothes based on room temperature
let temp = await tempPromise;
// Assume `changeClothes` also returns a Promise
if(temp > 20) {
await changeClothes("warm");
} else {
await changeClothes("cold");
}
await teethPromise;
}
In the example above, suppose teethPromise was rejected (Error: out of toothpaste!) before getRoomTemperature was fulfilled. In this case, there would be an unhandled Promise rejection until await teethPromise.
My point is this... if we consider unhandled Promise rejections to be a problem, Promises that are later handled by an await might get inadvertently reported as bugs. Then again, if we consider unhandled Promise rejections to not be problematic, legitimate bugs might not get reported.
Thoughts on this?
This is related to the discussion found in the Node.js project here:
Default Unhandled Rejection Detection Behavior
if you write the code this way:
function getReadyForBed() {
let teethPromise = brushTeeth();
let tempPromise = getRoomTemperature();
// Change clothes based on room temperature
return Promise.resolve(tempPromise)
.then(temp => {
// Assume `changeClothes` also returns a Promise
if (temp > 20) {
return Promise.resolve(changeClothes("warm"));
} else {
return Promise.resolve(changeClothes("cold"));
}
})
.then(teethPromise)
.then(Promise.resolve()); // since the async function returns nothing, ensure it's a resolved promise for `undefined`, unless it's previously rejected
}
When getReadyForBed is invoked, it will synchronously create the final (not returned) promise - which will have the same "unhandled rejection" error as any other promise (could be nothing, of course, depending on the engine). (I find it very odd your function doesn't return anything, which means your async function produces a promise for undefined.
If I make a Promise right now without a catch, and add one later, most "unhandled rejection error" implementations will actually retract the warning when i do later handle it. In other words, async/await doesn't alter the "unhandled rejection" discussion in any way that I can see.
to avoid this pitfall please write the code this way:
async function getReadyForBed() {
let teethPromise = brushTeeth();
let tempPromise = getRoomTemperature();
// Change clothes based on room temperature
var clothesPromise = tempPromise.then(function(temp) {
// Assume `changeClothes` also returns a Promise
if(temp > 20) {
return changeClothes("warm");
} else {
return changeClothes("cold");
}
});
/* Note that clothesPromise resolves to the result of `changeClothes`
due to Promise "chaining" magic. */
// Combine promises and await them both
await Promise.all(teethPromise, clothesPromise);
}
Note that this should prevent any unhandled promise rejection.
"DeprecationWarning: Unhandled promise rejections are deprecated"
TLDR: A promise has resolve and reject, doing a reject without a catch to handle it is deprecated, so you will have to at least have a catch at top level.
In my case was Promise with no reject neither resolve, because my Promise function threw an exception. This mistake cause UnhandledPromiseRejectionWarning message.
I was seeing this when I had a util file with a Promised API call, a component that calls it but wasn't explicitly handling the .catch, and a Jest that was mocking up a Promise.reject:
fetchStuff.mockImplementationOnce(() => Promise.reject(new Error('intentional fail')));
Furthermore, this was poisoning my mock, so that even though I was calling jest.resetAllMocks() before each test, the very next test would try render and that render would call the API, and it would fail. The test after would be back to a good state. I could swap around the order of my tests to prove that it would always poison the next render.
I tried handling the error in the API, but didn't succeed. I tried handling in my Jest mock, but that didn't work, either. What I ended up having to do was explicitly handle the .catch in my component.
I had faced a similar issue with NodeJS, where the culprit was a forEach loop. Note that forEach is a synchronous function (NOT Asynchronous). Therefore it just ignores the promise returned.
The solution was to use a for-of loop instead:
Code where I got the error:
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()
is as follows:
permissionOrders.forEach( async(order) => {
const requestPermissionOrder = new RequestPermissionOrderSchema({
item: order.item,
item_desc: order.item_desc,
quantity: order.quantity,
unit_price: order.unit_price,
total_cost: order.total_cost,
status: order.status,
priority: order.priority,
directOrder: order.directOrder
});
try {
const dat_order = await requestPermissionOrder.save();
res.json(dat_order);
} catch(err){
res.json({ message : err});
}
});
Solution for the above issue is as follows:
for (let order of permissionOrders){
const requestPermissionOrder = new RequestPermissionOrderSchema({
item: order.item,
item_desc: order.item_desc,
quantity: order.quantity,
unit_price: order.unit_price,
total_cost: order.total_cost,
status: order.status,
priority: order.priority,
directOrder: order.directOrder
});
try {
const dat_order = await requestPermissionOrder.save();
res.json(dat_order);
} catch(err){
res.json({ message : err});
}
};
Try not closing the connection before you send data to your database. Remove client.close(); from your code and it'll work fine.
When I instantiate a promise, I'm going to generate an asynchronous function. If the function goes well then I call the RESOLVE then the flow continues in the RESOLVE handler, in the THEN. If the function fails, then terminate the function by calling REJECT then the flow continues in the CATCH.
In NodeJs are deprecated the rejection handler. Your error is just a warning and I read it inside node.js github. I found this.
DEP0018: Unhandled promise rejections
Type: Runtime
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.
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