Async function always return undefined - javascript

im using nodejs 8. I've replaced promise structure code to use async and await.
I have an issue when I need to return an object but await sentence resolve undefined.
This is my controller method:
request.create = async (id, params) => {
try {
let request = await new Request(Object.assign(params, { property : id })).save()
if ('_id' in request) {
Property.findById(id).then( async (property) => {
property.requests.push(request._id)
await property.save()
let response = {
status: 200,
message: lang.__('general.success.created','Request')
}
return Promise.resolve(response)
})
}
}
catch (err) {
let response = {
status: 400,
message: lang.__('general.error.fatalError')
}
return Promise.reject(response)
}
}
In http request function:
exports.create = async (req, res) => {
try {
let response = await Request.create(req.params.id, req.body)
console.log(response)
res.send(response)
}
catch (err) {
res.status(err.status).send(err)
}
}
I tried returning Promise.resolve(response) and Promise.reject(response) with then and catch in the middleware function and is occurring the same.
What's wrong?
Thanks a lot, cheers

You don't necessarily need to interact with the promises at all inside an async function. Inside an async function, the regular throw syntax is the same as return Promise.reject() because an async function always returns a Promise. Another thing I noticed with your code is that you're rejecting promises inside a HTTP handler, which will definitely lead to unexpected behavior later on. You should instead handle all errors directly in the handler and act on them accordingly, instead of returning/throwing them.
Your code could be rewritten like so:
request.create = async (id, params) => {
let request = await new Request(Object.assign(params, { property : id })).save()
if ('_id' in request) {
let property = await Property.findById(id)
property.requests.push(request._id)
await property.save()
}
}
And your http handler:
exports.create = async (req, res) => {
try {
await Request.create(req.params.id, req.body)
res.send({
status: 200,
message: lang.__('general.success.created','Request')
})
} catch (err) {
switch (err.constructor) {
case DatabaseConnectionError: // Not connected to database
return res.sendStatus(500) // Internal server error
case UnauthorizedError:
return res.sendStatus(401) // Unauthorized
case default:
return res.status(400).send(err) // Generic error
}
}
}
Error classes:
class DatabaseConnectionError extends Error {}
class UnauthorizedError extends Error {}
Because you have that try/catch block inside your http handler method, anything that throws or rejects inside the Request.create method will be caught there. See https://repl.it/LtLo/3 for a more concise example of how errors thrown from async function or Promises doesn't need to be caught directly where they are first called from.

Related

Why isn't react component catching axios error

I have the following code on python using flask
#bp.route("/test", methods=["GET"])
def test():
throw_error = True
if throw_error :
return jsonify(message="Throwing an error"), 405
return jsonify(message="Test message"), 200
on React I have a context setup with the following function
function testRequest(){
const response = axios.get('/api/test')
console.log(response)
}
I'm calling this function on a button click in another component by
async function handleButtonClick(e){
e.preventDefault();
try{
await testRequest();
}catch(error) { // DOESN'T EXECUTE??
console.error("Error occured")
setError("Error occurred in test method")
}
}
Why isn't the try catch, catching the 405 error?
You can only usefully await a promise. testRequest doesn't return a promise.
It triggers axios.get, assigns the promise to response, logs it, then returns undefined.
The try/catch doesn't touch the promise at all.
You could fix that with:
function testRequest(){
const response_promise = axios.get('/api/test')
console.log(response_promise)
return response_promise;
}
So the promise is being awaited inside the try/catch.
With below modification to your function, you will be able to catch error.
async function testRequest() {
const response = await axios.get('http://localhost:1337/sample/test');
return response;
}
async function handleButtonClick(e:any) {
e.preventDefault();
try {
await testRequest();
} catch (error) { // DOESN'T EXECUTE??
console.error("Error occured")
console.log(error.mes);
}
}

How to properly use a class method that returns a promise?

I have a method in a class that returns a Firebase user document:
class FirebaseUtils {
async getUserDocument(uid) {
if (!uid) return null
try {
const userDocument = await this.firestore.collection('users').doc(uid).get()
return { uid, ...userDocument.data() }
} catch (error) {
console.error('error getting user document: ', error)
}
}
}
I am getting PromiseĀ {<pending>} when I try to get the result of this function in another file
//need to update userDocument later
const userDocument = firebaseUtils.getUserDocument(uid)
console.log(userDocument) //PromiseĀ {<pending>}
I've tried this as well ascreating an immediatelly invoked function to await the getUserDocument function but that didn't work.
Since async functions return a Promise, you'll need to await or use .then to get the resolved value:
const userDocument = await firebaseUtils.getUserDocument(uid)
console.log(userDocument)
or
firebaseUtils.getUserDocument(uid).then((userDocument) => {
console.log(userDocument)
})
As a side note, you'll probably want to return null after logging the error in the catch, or at least be aware of the fact that the function returns undefined in that case.

How can I use async lambdas without a try/catch block and still have custom error messages?

I'm trying to avoid wrapping all my awaited calls in an async lambda with a try catch. I want to catch and send custom error responses, but wrapping each awaited call in a try/catch is syntactically ugly compared to .catch(). Is there a way to do something like this:
exports.hanlder = async (event, context, callback) => {
const foo = await bar(baz).catch((error) => {
eventResponse.statusCode = 503;
eventResponse.body = JSON.stringify({ message: 'unable to bar' , error});
// normally we'd callback(null, eventResponse)
});
Without wrapping in try/catch like this?
exports.hanlder = async (event, context, callback) => {
let foo;
try {
foo = await bar(baz);
} catch (error) {
eventResponse.statusCode = 503;
eventResponse.body = JSON.stringify({ message: 'unable to bar', error});
return eventResponse;
}
// then do something else with foo
if (foo.whatever) {
// some more async calls
}
It's just not pretty to have a bunch of try/catch once you have like 7 awaited calls in a single lambda. Is there a prettier way to do it using the promise built-in .catch()?
The .catch() method is compatible with async/await and often less ugly if you want to rethrow an exception. I think you're looking for
exports.handler = async (event, context, callback) => {
try {
const foo = await bar(baz).catch(error => {
throw {message: 'unable to bar', error};
});
// do something with `foo`, and more `await`ing calls throwing other errors
// return a response
} catch(err) {
eventResponse.statusCode = 503;
eventResponse.body = JSON.stringify(err);
return eventResponse;
}
};

Async/Await error handling

I'm trying to handle a custom error that my async method throws, but the try catch block doesn't work appropriately.
I think the way I'm doing it should work but the error is not caught and the program terminates by displaying it in the terminal.
Here is where it throws the error:
async setupTap(tap) {
const model = this.connection.model('Tap', TapSchema);
await model.findOneAndUpdate({ id: tap.id }, tap, (err, result) => {
let error = null;
if (!result) {
throw new Error('Tap doesn\'t exists', 404);
}
return result;
});
}
Then, the error handling code:
async setupTapHandler(request, h) {
const tapData = {
id: request.params.id,
clientId: request.payload.clientId,
beerId: request.payload.beerId,
kegId: request.payload.kegId,
};
try {
await this.kegeratorApi.setupTap(tapData);
} catch (e) {
if (e.code === 404) return h.response().code(404);
}
return h.response().code(204);
}
Can someone help me?
I also looked at other topics:
Correct Try...Catch Syntax Using Async/Await
How to properly implement error handling in async/await case
You can only use await to successfully wait on an async operation if you are awaiting a promise. Assuming you are using mongoose, I don't know mongoose really well, but it appears that model.findOneAndUpdate() does not return a promise if you pass it a callback. Instead, it executes and puts the result in the callback.
In addition, doing a throw from a callback like this just throws into the database (the code that called the callback) and won't do you any good at all. To have a throw make a rejected promise, you need to either be throwing from the top level of an async function or be throwing from inside a .then() or .catch() handler or inside a promise executor function. That's where throw makes a promise rejected.
The key here is that you want to use the promise interface to your database, not the callback interface. If you don't pass a callback, then it returns a query which you can use .exec() on to get a promise which you can then use with await.
In addition, you weren't building an error object that would have a .code property set to 404. That isn't a property that is supported by the Error object constructor, so if you want that property, you have to set it manually.
I'd suggest this:
async setupTap(tap) {
const model = this.connection.model('Tap', TapSchema);
let result = await model.findOneAndUpdate({ id: tap.id }, tap).exec();
if (!result) {
let err = new Error('Tap doesn\'t exists');
err.code = 404;
throw err;
}
return result;
}
Or, with only one async operation here, there's really not much benefit to using await. You could just do this:
setupTap(tap) {
const model = this.connection.model('Tap', TapSchema);
return model.findOneAndUpdate({ id: tap.id }, tap).exec().then(result => {
if (!result) {
let err = new Error('Tap doesn\'t exists');
err.code = 404;
throw err;
}
return result;
});
}
The function findOneAndUpdate returns a promise so there should be no need for the callback. If a callback is needed and you can't update to a newer version then maybe wrap calls in a promise (under To use a callback api as promise you can do:)
Then you want to set code on error, you can't do that with the constructor.
async setupTap(tap) {
const model = this.connection.model('Tap', TapSchema);
const result = await model.findOneAndUpdate({ id: tap.id }, tap);
if (!result) {
const e = new Error('Tap doesn\'t exists');
e.code = 404;
throw(e);
}
return result;
}
async setupTapHandler(request, h) {
const tapData = {
id: request.params.id,
clientId: request.payload.clientId,
beerId: request.payload.beerId,
kegId: request.payload.kegId,
};
try {
await this.kegeratorApi.setupTap(tapData);
} catch (e) {
if (e.code === 404) return h.response().code(404);
}
return h.response().code(204);
}

How to set default rejected promise behavior for all my Express middlewares?

I'm using promises inside express middleware. I want to use the async/await methods.
app.get('/data1',async function(req,res) {
data = await getData1(); // This line throw an error,
res.send(data)
})
app.get('/data2',async function(req,res) {
data = await getData2(); // This line throw an error
res.send(data)
})
This makes the browser wait forever.
On the server I see
(node:251960) UnhandledPromiseRejectionWarning: Unhandled promise rejection
Now, to fix it for one middleware I'm doing:
app.get('/data1',async function (req,res){
return (async function(){
data = await getData1()
})().catch(() => {
res.send("You have an error")
}
})
app.get('/data2',async function (req,res){
return (async function(){
data = await getData2()
})().catch(() => {
res.send("You have an error")
}
})
I don't like this repetion. How can I set default error? I have tried for example:
app.use(function(error,req,res,next)){
res.send('You have an error')
}
But it didn't work.
In other words: How to set default function to be called when Express middlewares returning a rejected promise?
Now I found a way how to do it, I'm still keep the question open for more suggestions
app.get("/data1",
wrap_middleware(async (req, res) => {
data1=await getData1()
res.send(data1)
})
}
app.get("/data1",
wrap_middleware(async (req, res) => {
data2=await getData2()
})
}
function wrap_middleware(func) {
return async (req, res, next) => {
func(req, res, next).catch(err => {
console.log(err.message);
res.send("Error");
});
};
}
I don't understand the use of sending the same error for different function but I think the handling error code could be write in more readable way (just catch the error and do with them what you want the same way you catch errors in any route middleware):
function getData1(){
return new Promise( (resolve,reject) => {
setTimeout(() =>{
reject(new Error('error has occur!'));
},2000);
});
}
router.get('/data1', async (req,res, next) => {
try{
const data = await getData1();
res.send(data);
}
catch(ex){
res.send(ex.message);
// next(ex); => sending it to express to handle it
}
});
If you want a global error handling then its not any different from any code you want catch errors globally - you can set a function that take as param , the response object and the async code and create general catch for every async call comes from middleware (which has response object)

Categories