asynchronous function in promise throws error and doesn't reject [duplicate] - javascript

This question already has answers here:
Asynchronous exception handling with bluebird promises
(3 answers)
Closed 4 years ago.
If there is a promise with an asynchronous function in it and if in the asynchronous function an error happens the promise doesn't catch but throws an error and crashes the application, which I don't understand.
Obviously I would like to handle the error, do you know why does the promise behave like this and what would be a way around it?
thanks
// this promise will have an error since param is not defined,
// and the promise won't be caught
function randomPromise(param) {
return new Promise((resolve, reject) => {
setTimeout(() => {
param[0] = 11;
}, 2000);
});
}
randomPromise()
.then(() => {
console.log('nothing');
})
.catch((e) => {
console.log('with set timeout or any async function in the promise, the error caused by \'param[0] = 11;\' wont bring the control here into the catch block just throws an error and crashes the application');
console.log(e);
});
// this promise will have an error since param is not defined
// but the promise will be caught
function randomPromiseGoesToCatchBlock(param) {
return new Promise((resolve, reject) => {
param[0] = 11;
});
}
randomPromiseGoesToCatchBlock()
.then(() => {
console.log('nothing');
})
.catch((e) => {
console.log('without the setTimeout function or any async function the error caused by \'param[0] = 11;\' brings the control here into the catch block');
console.log(e);
});

Errors that are thrown inside a Promise constructor and that occur asynchronously need to be explicitly try/catched so that reject can be called, so that the Promise control flow can be transferred to the Promise's catch. For example:
// this promise will have an error since param is not defined, and the promise wont be catched
function randomPromise(param) {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
param[0] = 11;
} catch(e) {
reject(e);
}
}, 2000);
});
}
randomPromise()
.catch((e) => {
console.log(e.message);
});
Otherwise, neither resolve nor reject will be called, and the error was asynchronous, so the thread the Promise was created on has already ended, so the interpreter doesn't know that the thrown error should reject that Promise without you explicitly telling it to.
In contrast, errors thrown synchronously inside a Promise constructor will automatically result in the constructed Promise rejecting immediately.

Related

Calling .finally on a promise that is try/catch handled elsewhere causes UnhandledPromiseRejection in nodejs [duplicate]

This question already has answers here:
Weird behavior with Promise throwing "Unhandled promise rejection" error
(1 answer)
Uncaught (in promise)
(2 answers)
How is PromiseRejectionEvent triggered?
(2 answers)
UnhandledPromiseRejectionWarning after clearTimeout [duplicate]
(1 answer)
Closed 5 months ago.
I've recently encountered a weird behavior with nodejs. I'll explain with the following example:
let's say we have two functions, foo and bar, foo creates a promise, calls .finally() on it, and returns it. bar on the other hand, wraps the promise with a try/catch.
const foo = () => {
const promise = new Promise((resolve, reject) => {
reject('my-error');
});
promise.finally(() => {
console.log('at foo');
});
return promise;
}
const bar = async () => {
try {
const result = await foo();
} catch (e) {
console.log('at bar: ', e);
}
}
bar();
the output would be:
at foo
at bar: my-error
node:internal/process/promises:279
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: 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(). The promise rejected with the reason "my-error".] {
code: 'ERR_UNHANDLED_REJECTION'
}
From the output, we can see that the error is handled by bar function, yet an UnhandledPromiseRejection error is thrown.
However, if we replace .finally() with .catch() not such error happen.
const foo = () => {
const promise = new Promise((resolve, reject) => {
reject('my-error');
});
promise.catch(() => {
console.log('at foo');
});
return promise;
}
I'm not sure what's happening over here; I'd appreciate it if someone could help me visualize what's happening.
then, catch, and finally create new promises that are resolved to the promise you call them on and the result of the handler function you pass into them. (If resolved to is slightly unfamiliar, see my blog post on promise terminology.) The unhandled rejection is from the promise created by finally, which was rejected because the promise it was resolved to was rejected. Nothing in your code handles the rejection of that promise, just the other one.
If you want to use finally on the promise created in foo, return the resulting promise instead of the original one:
const foo = () => {
const promise = new Promise((resolve, reject) => {
reject('my-error');
});
// vvvvvv−−−−−−−−−−−−−−−−−−−−−−
return promise.finally(() => {
console.log('at foo');
});
}
const bar = async () => {
try {
const result = await foo();
} catch (e) {
console.log('at bar: ', e);
}
}
bar();

Avoid uncaught exception when a promise gets rejected due to timeout?

I have a situation in my node.js program where I have an array of promises. I am prepared to wait a maximum of 200 ms for each promise in the array to get fulfilled, if it’s not fulfilled by then I want it to be rejected.
The code I have written for this works when I run my script in the terminal using node.js without a debugger attached.
However, when I debug the same script using VS code it stops as soon as a promise gets rejected due to timeout. The debugger claims that the rejection is an uncaught exception.
How can I change the code I have such that it does exactly what it does now, but a rejected promise doesn’t cause an exception?
I have tried adding try{} catch{} all over the place but cannot seem to find a solution.
Here is a minimal reproducible example of my issue (the debugger complains about the line reject( "timeout" ) ):
async function delayedPromise(delay) {
await new Promise((res) => setTimeout(res, delay));
return "success";
}
function rejectAfterDelay(ms) {
return new Promise((_, reject) => setTimeout(() => {
reject("timeout");
}, ms));
}
async function main() {
// Create array of promises.
promArr = [];
promArr.push(delayedPromise(100));
promArr.push(delayedPromise(200));
promArr.push(delayedPromise(300));
promArr.push(delayedPromise(400));
promArr.push(delayedPromise(500));
// Wait for all promises to either get fulfilled or get rejected after 200 ms.
const msMaxTime = 200;
const result = await Promise.allSettled(
promArr.map(promise => Promise.race([promise, rejectAfterDelay(msMaxTime)]))
);
console.log(result);
}
main()
Instead of racing a promise with a short-lived promise(rejectAfterDelay), we can wrap the promise in a short-lived promise:
async function delayedPromise(delay) {
return new Promise((res) => setTimeout(res, delay, 'success'));
}
// wrap the promise instead of racing it
function rejectAfterDelay(promise, ms) {
return new Promise((resolve, reject) => {
setTimeout(reject, ms, 'timeout');
// forward the reasons to the wrapper
promise.then(reason => resolve(reason))
.catch(err => reject(err));
});
}
async function main() {
// Create array of promises.
promArr = [];
promArr.push(delayedPromise(100));
promArr.push(delayedPromise(200));
promArr.push(delayedPromise(300));
promArr.push(delayedPromise(400));
promArr.push(delayedPromise(500));
// Wait for all promises to either get fulfilled or get rejected after 200 ms.
const msMaxTime = 200;
const result = await Promise.allSettled(
promArr.map(promise => {
//return Promise.race([promise, rejectAfterDelay(msMaxTime)]);
return rejectAfterDelay(promise, msMaxTime);
})
);
console.log(result.map(r => r.value ? r.value : r.reason));
}
main()
With this the debugger doesn't complain when Uncaught Exceptions option is selected.
Also, depending on your situation, instead of setTimeout(reject, ms, 'timeout') you can use setTimeout(resolve, ms, 'timeout') to make it fail gracefully.

Different behaviour between 'returning a promise' and 'resolving with promise' inside a `new`-generated promise

It's not much I discovered Javascript Promise.
However I found out a behaviour I couldn't understand concerning the nesting of (new or returned) Promise inside Promises
That's the background (extPromiseX is a third-party function returning Promise):
Case 1:
function myAction(param) {
return extPromise1(sql).then((result) => {
[...]
return extPromise2(sql2).then((result) => {
[...]
return extPromise3(sql3);
})
});
}
// Main Process
myAction(...)
.then(() => {
console.log('Process Terminated');
}).catch((error) => {
console.error('Exit with error', error);
});
Now, as expected, I got from the console, in
1) extPromise1 completed
2) extPromise2 completed
3) extPromise3 completed
4) Process Terminated
Case 2:
function myAction(param) {
return new Promise(function() {
if (itsAllOkWithInputs) {
// Some sync code here
return extPromise1(sql).then((result) => {
[...]
return extPromise2(sql2).then((result) => {
[...]
return extPromise3(sql3);
})
})
} else {
throw 'Something went wrong';
}
});
}
// Main process
myAction(...)
.then(() => {
console.log('Process Terminated');
}).catch((error) => {
console.error('3) --> Exit with error', error);
})
In this second case extPromises are executed but the very first Promise remains pending (confirmed by debug). So the console shows:
1) extPromise1 completed
2) extPromise2 completed
3) extPromise3 completed
I empirically realized that I had to change myAction function as follow for the code to work:
function myAction(param) {
return new Promise(function(resolve, reject) {
if (itsAllOkWithInputs) {
// Some sync code here
let ep = extPromise1(sql).then(...);
resolve(ep);
} else {
throw 'Something went wrong';
}
});
}
My question:
I thought returning a promise inside another parental one would make
the parent resolving with the results of the child. This is the case
inside the then code block applied to the external promises. Why
this is not valid for the new Promise case?
Because .then is meant to chain promises. You can return a Promise from inside the then callback, and then itself will return a new Promise.
The Promise constructor is supposed to construct a Promise from an underlying callback. If you return a Promise from inside a Promise constructor, you are doing something wrong conceptually. And that's why it does not work.
function myAction(param) {
if (itsAllOkWithInputs) {
return extPromise1(sql).then(...);
} else {
return Promise.reject('Something went wrong');
}
}
// OR
async function myAction(param) {
if (itsAllOkWithInputs) {
await extPromise1(sql);
} else {
throw 'Something went wrong';
}
}
new Promise(function() {
You are not using neither resolve nor reject arguments (you actually even haven't declared them), so the promise will never get resolved or rejected. All other stuff is irrelevant.
If your function returns promise, you have to simply call it and return a result. If you don't know whether the function return promise or just a value, you can wrap it's call into Promise.resolve, but not in new Promise.

Optional catch in javascript promises

The following is valid:
new Promise<void>((resolve, reject) => {
reject()
})
.then(() => {})
.catch(() => {})
But I might not always care about the error. Is there a way to make the catch optional?
I tried this but it didn't work:
new Promise<void>((resolve, reject?) => {
if (reject) reject()
})
.then(() => {})
Error: Uncaught (in promise): undefined
Is there a way to make the catch optional?
No. If you are using a promise that might error, you need to handle that (or propagate it to your caller).
Of course if you create the promise yourself, rejecting it is optional, and you can choose to never reject your promises so that you won't need to handle any errors. But if there are errors from promises that you are using, and you want to ignore them, you must do so explicitly. Just write
somePromise.catch(e => void e);
// or () => { /* ignore */ }
// or function ignore() {}
I was trying to solve the same issue, and finally come up with the following promise wrapper:
/**
* wraps a given promise in a new promise with a default onRejected function,
* that handles the promise rejection if not other onRejected handler is provided.
*
* #param customPromise Promise to wrap
* #param defaultOnRejected Default onRejected function
* #returns wrapped promise
*/
export function promiseWithDefaultOnRejected(customPromise: Promise<any>, defaultOnRejected: (_: any) => any): Promise<any> {
let hasCatch = false;
function chain(promise: Promise<any>) {
const newPromise: Promise<any> = new Promise((res, rej) => {
return promise.then(
res,
function(value) {
if (hasCatch) {
rej(value);
} else {
defaultOnRejected(value);
}
},
);
});
const originalThen = newPromise.then;
// Using `defineProperty` to not overwrite `Promise.prototype.then`
Object.defineProperty(newPromise, 'then', {
value: function (onfulfilled: any, onrejected: any) {
const result: Promise<any> = originalThen.call(newPromise, onfulfilled, onrejected);
if (typeof onrejected === 'function') {
hasCatch = true;
return result;
} else {
return chain(result);
}
}
});
return newPromise;
}
return chain(customPromise);
}
This function lets you wrap your promises with a defaultOnRejected function that will handle the rejected promise if no other handler is provided. For example:
const dontCare = promiseWithDefaultOnRejected(Promise.reject("ignored"), () => {});
The result promise will never throw an "Unhandled Promise Rejection", and you can use it as follows:
dontCare.then(x=>console.log("never happens")).catch(x=>console.log("happens"));
or
dontCare.then(x=>console.log("never happens"), x=>console.log("happens"));
or simply without onRejected handler:
dontCare.then(x=>console.log("never happens")).then(x=>console.log("also never happens"));
An issue with this util is that it is not working as expected with async/await syntax: you need to propagate and handle the "catch" path as follows:
async () => {
try {
await promiseWithDefaultOnRejected(Promise.reject("ignored"), () => {})
.catch((e) => { throw e; });
} catch (e) {
console.log("happens");
}
}
You could resolve when the error is something you don't care about. If your catch returns anything other than a rejected promise, the error isn't propagated down the chain.
const ignorableError = new Error("I don't care about this error");
const myPromise = new Promise((resolve, reject) => {
reject(ignorableError);
})
.then(() => {})
.catch(error => {
if(error == ignorableError) {
console.log("Error ignored");
return;
}
// Do something else...
});
myPromise.then(() => console.log("Success"))
Let me try to describe your situation:
You have a service that gets user information and a function called getUser that uses that service. When the service fails for any reason then getUser does not have a user available. The result of getUser is used quite a lot of times in your code with the following situation(s):
User is available run a function (block of code).
User is not available run a function (block of code).
Run a function with the error/reject of the service.
When using getUser result you may want to run all 3 functions, a combination of 2 of them or only one.
Current getUser returns a Promise and this type does not seem to be suitable for your situation. Mainly because rejecting a promise and not catching it will cause unhandled promise rejection. And because if you want to run code if user is available or not it complicates the functions (they both have to check the result instead of assuming user is or is not available).
Maybe you can try the following code, please be careful making assumptions in the not available block, this could be due to any error. For example: it does not mean the user does not exist because it could be a network error.
In the example getUser is used but can be any function that returns a promise where you assume not available on reject.
const isAvailable = promise => {
//do not expose NOT_AVAILABLE outside this function
const NOT_AVAILABLE = {type:"not available"};
//no uncaught promise rejection errors by VM
const savePromise = promise.catch(x=>x);
return {
available:fn=>
promise
.catch(e=>Promise.reject(NOT_AVAILABLE))
.then(fn)
.catch(
e=>
(e===NOT_AVAILABLE)
? undefined//ignore
: Promise.reject(e)//re throw, error is not from service
),
//call not available with the promise if promise rejects
notAvailable:fn=>promise.catch(()=>fn(promise)),
catchError:promise.catch.bind(promise)
};
}
const service = arg =>
(arg===1)
? Promise.resolve(arg)
: Promise.reject(arg)
const getUser = arg => isAvailable(service(arg));
var user = getUser(2);
//if service failed available will be skipped
user.available(
user=>console.log("skipped:",user)
);
//both catch and notAvailable will be called
user.notAvailable(
arg=>console.log("not available:",arg)
);
user.notAvailable(
arg=>console.log("still not available:",arg)
);
//not handling catchError does not cause uncaught promise exception
// but you can inspect the error
// user.catchError(
// err=>console.log("error is::",err)
// );
var user = getUser(1);
//you can call available on user multiple times
user.available(
user=>console.log("got user:",user)
);
user.available(
user=>Promise.resolve(user)
.then(user=>console.log("still got user:",user))
.then(x=>Promise.reject("have to catch this one though"))
.catch(e=>console.log("ok, got error trying to run available block:",e))
);
//showing you can inspect the error
var user = getUser(5);
user.catchError(err=>console.log("error from service:",err));

Getting a UnhandledPromiseRejectionWarning when testing using mocha/chai

So, I'm testing a component that relies on an event-emitter. To do so I came up with a solution using Promises with Mocha+Chai:
it('should transition with the correct event', (done) => {
const cFSM = new CharacterFSM({}, emitter, transitions);
let timeout = null;
let resolved = false;
new Promise((resolve, reject) => {
emitter.once('action', resolve);
emitter.emit('done', {});
timeout = setTimeout(() => {
if (!resolved) {
reject('Timedout!');
}
clearTimeout(timeout);
}, 100);
}).then((state) => {
resolved = true;
assert(state.action === 'DONE', 'should change state');
done();
}).catch((error) => {
assert.isNotOk(error,'Promise error');
done();
});
});
On the console I'm getting an 'UnhandledPromiseRejectionWarning' even though the reject function is getting called since it instantly shows the message 'AssertionError: Promise error'
(node:25754) UnhandledPromiseRejectionWarning: Unhandled promise
rejection (rejection id: 2): AssertionError: Promise error: expected
{ Object (message, showDiff, ...) } to be falsy
should transition with the correct event
And then, after 2 sec I get
Error: timeout of 2000ms exceeded. Ensure the done() callback is
being called in this test.
Which is even weirder since the catch callback was executed(I think that for some reason the assert failure prevented the rest of the execution)
Now the funny thing, if I comment out the assert.isNotOk(error...) the test runs fine without any warning in the console. It stills 'fails' in the sense that it executes the catch.
But still, I can't understand these errors with promise. Can someone enlighten me?
The issue is caused by this:
.catch((error) => {
assert.isNotOk(error,'Promise error');
done();
});
If the assertion fails, it will throw an error. This error will cause done() never to get called, because the code errored out before it. That's what causes the timeout.
The "Unhandled promise rejection" is also caused by the failed assertion, because if an error is thrown in a catch() handler, and there isn't a subsequent catch() handler, the error will get swallowed (as explained in this article). The UnhandledPromiseRejectionWarning warning is alerting you to this fact.
In general, if you want to test promise-based code in Mocha, you should rely on the fact that Mocha itself can handle promises already. You shouldn't use done(), but instead, return a promise from your test. Mocha will then catch any errors itself.
Like this:
it('should transition with the correct event', () => {
...
return new Promise((resolve, reject) => {
...
}).then((state) => {
assert(state.action === 'DONE', 'should change state');
})
.catch((error) => {
assert.isNotOk(error,'Promise error');
});
});
For those who are looking for the error/warning UnhandledPromiseRejectionWarning outside of a testing environment, It could be probably because nobody in the code is taking care of the eventual error in a promise:
For instance, this code will show the warning reported in this question:
new Promise((resolve, reject) => {
return reject('Error reason!');
});
(node:XXXX) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Error reason!
and adding the .catch() or handling the error should solve the warning/error
new Promise((resolve, reject) => {
return reject('Error reason!');
}).catch(() => { /* do whatever you want here */ });
Or using the second parameter in the then function
new Promise((resolve, reject) => {
return reject('Error reason!');
}).then(null, () => { /* do whatever you want here */ });
I got this error when stubbing with sinon.
The fix is to use npm package sinon-as-promised when resolving or rejecting promises with stubs.
Instead of ...
sinon.stub(Database, 'connect').returns(Promise.reject( Error('oops') ))
Use ...
require('sinon-as-promised');
sinon.stub(Database, 'connect').rejects(Error('oops'));
There is also a resolves method (note the s on the end).
See http://clarkdave.net/2016/09/node-v6-6-and-asynchronously-handled-promise-rejections
The assertion libraries in Mocha work by throwing an error if the assertion was not correct. Throwing an error results in a rejected promise, even when thrown in the executor function provided to the catch method.
.catch((error) => {
assert.isNotOk(error,'Promise error');
done();
});
In the above code the error objected evaluates to true so the assertion library throws an error... which is never caught. As a result of the error the done method is never called. Mocha's done callback accepts these errors, so you can simply end all promise chains in Mocha with .then(done,done). This ensures that the done method is always called and the error would be reported the same way as when Mocha catches the assertion's error in synchronous code.
it('should transition with the correct event', (done) => {
const cFSM = new CharacterFSM({}, emitter, transitions);
let timeout = null;
let resolved = false;
new Promise((resolve, reject) => {
emitter.once('action', resolve);
emitter.emit('done', {});
timeout = setTimeout(() => {
if (!resolved) {
reject('Timedout!');
}
clearTimeout(timeout);
}, 100);
}).then(((state) => {
resolved = true;
assert(state.action === 'DONE', 'should change state');
})).then(done,done);
});
I give credit to this article for the idea of using .then(done,done) when testing promises in Mocha.
I faced this issue:
(node:1131004) UnhandledPromiseRejectionWarning: Unhandled promise rejection (re
jection id: 1): TypeError: res.json is not a function
(node:1131004) DeprecationWarning: Unhandled promise rejections are deprecated.
In the future, promise rejections that are not handled will terminate the Node.j
s process with a non-zero exit code.
It was my mistake, I was replacing res object in then(function(res), so changed res to result and now it is working.
Wrong
module.exports.update = function(req, res){
return Services.User.update(req.body)
.then(function(res){//issue was here, res overwrite
return res.json(res);
}, function(error){
return res.json({error:error.message});
}).catch(function () {
console.log("Promise Rejected");
});
Correction
module.exports.update = function(req, res){
return Services.User.update(req.body)
.then(function(result){//res replaced with result
return res.json(result);
}, function(error){
return res.json({error:error.message});
}).catch(function () {
console.log("Promise Rejected");
});
Service code:
function update(data){
var id = new require('mongodb').ObjectID(data._id);
userData = {
name:data.name,
email:data.email,
phone: data.phone
};
return collection.findAndModify(
{_id:id}, // query
[['_id','asc']], // sort order
{$set: userData}, // replacement
{ "new": true }
).then(function(doc) {
if(!doc)
throw new Error('Record not updated.');
return doc.value;
});
}
module.exports = {
update:update
}
Here's my take experience with E7 async/await:
In case you have an async helperFunction() called from your test... (one explicilty with the ES7 async keyword, I mean)
→ make sure, you call that as await helperFunction(whateverParams) (well, yeah, naturally, once you know...)
And for that to work (to avoid ‘await is a reserved word’), your test-function must have an outer async marker:
it('my test', async () => { ...
I had a similar experience with Chai-Webdriver for Selenium.
I added await to the assertion and it fixed the issue:
Example using Cucumberjs:
Then(/I see heading with the text of Tasks/, async function() {
await chai.expect('h1').dom.to.contain.text('Tasks');
});
Just a heads-up that you can get a UnhandledPromiseRejectionWarning if you accidentally put your test code outside of the it-function. 😬
describe('My Test', () => {
context('My Context', () => {
it('should test something', () => {})
const result = testSomething()
assert.isOk(result)
})
})

Categories