I'm using nodejs request package (2.88 version) with node v10.24.1
The Promise below does not handle an error if the url simply was not specified. I'm getting UnhandledPromiseRejectionWarning and from the request package source I can see an exception thrown for that case.
If I remove the async keyword everything works fine, and the reject handled.
return new Promise(async (resolve, reject) => {
request(uri: url).on('error', err => {
reject();
}).on('response', response => {
resolve();
});
});
Could you please explain what happens. Probably the request's exception finished before the reject actually fired. Any details please.
When you pass a function to new Promise, this function gets immediately executed.
There's no magic here, for the purpose of illustration, the constructor of new Promise() might look a bit like this:
class Promise {
constructor(executor) {
executor(this.resolveFunction, this.rejectFunction);
}
}
So this example:
new Promise(() => {
console.log(1);
});
console.log(2);
Outputs:
1
2
This is important, because if you pass something to new Promise that throws an exception, this happens immediately too:
new Promise((res, rej) => {
throw new Error('foo');
});
The error thrown here doesn't cause the promise to be rejected, it completely prevents new Promise() from succeeding. No promise is returned, and the exception is thrown in the outer function that called new Promise().
However, this breaks down when you pass an async function:
new Promise(async (res, rej) => {
throw new Error('foo');
});
Because of how async functions work, this error will not be thrown during the setup of new Promise(), but the async function you passed will handle the error asyncronously and returns a rejected promise, that new Promise() doesn't know how to handle.
This is no different from calling an async function directly yourself and not handling errors:
(async () => {
throw new Error('foo');
})();
So the reason things are failing have less to do with new Promise(), but more with the fact how async functions work in general.
Just a last time for future readers, you should never pass an async executor to new Promise(), because:
The only reason you need async at all is so you can use await.
await only works with promises.
If you already had a promise you wanted to await, you didn't need new Promise() to begin with, because the main use-case of new Promise is to convert something that doesnt use promises into something that does.
Related
I can not understand how this thing works. can anyone explain please
`
module.exports = (thefunc) => (req,res,next)=>{
Promise.resolve(thefunc(req,res,next)).catch(next)
}
`
i searched on internet and still can not understand.
The point of this structure:
Promise.resolve(thefunc(req,res,next)).catch(next)
Is to catch any rejected promise that might be returned from calling:
thefunc(req,res,next)
And, direct the reject reason to call next(err). It is wrapped in Promise.resolve() in case thefunc(req,res,next) does not return a promise at all (perhapes even returns nothing). This makes it type-safe to add the .catch().
This attempts to add a bit of promise-awareness to your routing engine since Express, by itself, does not have promise-awareness for route handlers.
So, this will work with any of these situations inside of thefunc():
async function(req, res, next) {
if (!req.body.username) throw new Error("User Missing");
....
}
In this case, the function use here is declared as async so when it uses throw, that will cause it reject the promise that it returns. That will then end up call next(err) where err is the Error object we created there.
function(req, res, next) {
return queryMyDb(...).then(result => {
res.send(result);
});
}
In this case, if the DB query rejects, then this function will be returning a promise that rejects and the reject reason will be sent to next(err) just like in the previous example by the wrapper.
function(req, res, next) {
res.send({cntr: req.body.cntr++});
}
While this request handler doesn't cause any sort of error (assuming req.body.cntr is valid), it also doesn't return a promise. But, because the function call was wrapped in Promise.resolve(theFunc(...)) that still allows the wrapper to use the .catch() which won't actually come into play because no error occurs here, but adding the .catch() won't cause a run-time error because of the Promise.resolve() wrapper.
Note, the wrapper as shown will not catch synchronous exceptions thrown in the route handler unless the route handler is declared async (which auto-converts synchronous exceptions to a rejected promise for you).
If you want to also catch synchronous exceptions (generally advisable), you can modify it to this:
module.exports = (thefunc) => (req,res,next)=>{
try {
Promise.resolve(thefunc(req,res,next)).catch(next)
} catch(e) {
next(e);
}
}
Though, at that point, I would probably do this:
module.exports = async (thefunc) => (req,res,next)=>{
try {
await thefunc(req,res,next);
} catch(e) {
next(e);
}
}
Since it is harmless to use await on a non-promise, you don't have to wrap with Promise.resolve() when using await like this. If thefunc() happens to return a promise that rejects or happens to throw synchronously, the try/catch will catch either.
I'm working on a module containing a class with functions which calls an api everytimes we use a function.
I want to handle the result & catch IN the module and only send data in return.
function getAllFromTable(aTableName){
const request = {
.... aTableName
}
return apiCall(request)
}
function apiCall(requestConfig){
axios(requestConfig).then(result => {
return result.data
}
.catch(err => {
return err
}
}
That's the idea of what I would like to do but of course this only sends back "undefined"..
Is there a way to make a return in a then() to send back the data ?
Or another way to send back only the data and not a Promise to the one who calls the function "getAllFromTable()" ?
Is there a way to make a return in a then() to send back the data ? Or another way to send back only the data and not a Promise to the one who calls the function "getAllFromTable()" ?
No. In Javascript, there is NO way to return an asynchronously retrieved result directly. You HAVE to use an asynchronous mechanism such as a promise, a callback or an event to communicate back the asynchronous result. Since you already have a promise, you need to just return the promise and make sure that its resolved value is your final result. Then, the caller can use either .then() or await to get the result out of the promise.
That's the idea of what I would like to do but of course this only sends back "undefined"..
In your apiCall() function, you have to return the promise and fix your error handling.
Change this:
function apiCall(requestConfig){
axios(requestConfig).then(result => {
return result.data
}.catch(err => {
return err
});
}
to this:
function apiCall(requestConfig){
return axios(requestConfig).then(result => {
return result.data;
});
}
I removed the .catch() because you were changing the promise from rejected to resolved and making the error object be the resolved value. That is unlikely what you want for the function behavior and very unlikely what the caller wants. If you want a .catch() to log the error or something, then you can throw the error again to make sure the promise stays rejected.
function apiCall(requestConfig){
return axios(requestConfig).then(result => {
return result.data;
}).catch(err => {
// log error and rethrow so the promise stays rejected
console.log(err);
throw err;
});
}
The caller of apiCall() will then either use .then() or await to get the resolved value of the promise that the function returns.
apiCall(...).then(val => {
console.log(val);
}).catch(err => {
console.log(err);
});
I have a use case to resolve a Promise without returning it. Catching for errors internally, but don't want the caller to wait for the promise to resolve.
doSomething()
{
Promise.resolve()
.then(() => {
// do something.
})
.catch(reason => {
this.logger.error(reason);
});
}
Getting this error:
(node:2072) Warning: a promise was created in a handler at internal/timers.js:439:21 but was not returned from it, see http://. goo.gl/rRqMUw
at Function.Promise.cast (.../node_modules/bluebird/js/release/promise.js:225:13)
Just return something from the Promise callback where you are creating the fire and forget promise.
I'm guessing that handler is doSomething
doSomething()
{
Promise.resolve()
.then(() => {
// do something.
})
.catch(reason => {
this.logger.error(reason);
});
return null //or anything else that's sensible
}
Note: We usually ignore the error message, but sometimes they contain valuable information. In your error there's a link http://. goo.gl/rRqMUw that explains exactly this problem:d
I am using this chunk of code:
assert.throws(async () => {
patientSubscriber = await PatientSubscriber.create({
isSubscribed: true,
patient: patient._id,
subscriber: user._id
});
},{
message: /foo/
});
when I run it I get this:
AssertionError [ERR_ASSERTION]: Missing expected exception.
The assert API is:
https://nodejs.org/api/assert.html#assert_assert_throws_fn_error_message
Maybe I am hitting this issue?
https://github.com/nodejs/node-v0.x-archive/issues/8368
Anyone know what that error is about?
The error means that there was no exception while it was expected. async functions are syntactic sugar for promises. They never throw exceptions but may return rejected promise.
Since the assertion is expected to be synchronous, it's impossible to assert promises this way; the entire callstack should be promise-based.
As it was mentioned in comments, assert.rejects appeared in Node 10 and results in rejected promise on assertion fail.
In case promises are handled by a caller (e.g. in most testing frameworks), rejected promise needs to be returned from current function:
it('...', async () {
await assert.rejects((async () => {})()); // current function returns rejected promise
});
In case assertion promises aren't handled (e.g. in production), they result in UnhandledPromiseRejectionWarning console output, not in exceptions. unhandledRejection handler can be set to handle asynchronous errors:
process.on('unhandledRejection', err => {
console.error(err);
process.exit(1);
});
...
assert.rejects((async () => {})());
As part of an Node.js/Express API I am developing, I have built a custom error handler that lets me throw specific errors when needed e.g. BadRequest or NotFound etc.
The problem occurs when I wish to throw within a promise chain. For example:
db.doSomethingAsync().then(data => {
if(data.length === 0){
throw new errors.ResourceNotFound({ resource: "foo" });
}
});
After much reading on the topic, I see that this error will be swallowed by the promise and hence causes an unhandled Promise rejection.
I am aware I can reject however im not sure how I can then handle the specific error (rather than a catchall on reject which I dont want).
Also, to reject, would I not need to create a new promise inside of my promise chain? That feels messy.
Can anyone advise how to handle throwing a specific exception within a promise chain?
In addition to the first callback to then() which is resolve callback and recieves data, you could also provide a second callback to it which is reject callback and it recieves errors. so you could catch this specific error on the second callback of the next then:
db
.doSomethingAsync()
.then(data => {
if(data.length === 0){
throw new errors.ResourceNotFound({ resource: "foo" });
}
})
.then(massagedData => {
// work with massagedData here
}, err => {
// handle err here which is previous thrown error
// assert.ok( err instanceof errors.ResourceNotFound() )
})
.catch(err => {
// this will catch unhandled errors if any
});
then() by default returns a promise and if any of its callbacks ( whether the first or the second one ) throws an error then it returns a rejected promise and its reason will caught by next reject callback if any or by catch at the end. So you don't need to create a new promise.
See Promise.prototype.then()