Async/Await not behaving as expected - javascript

I'm learning on how to use Async/Await, and as far as I know it should wait until Await is done with executing and then go on with the code.
However, my code completely stops executing after my Await, this is my method (I'm using Vue):
async register () {
await this.form.validateFields((err, values) => {
if (err) {
return
}
alert('this gets executed if err is empty')
})
alert('this never gets get executed, why?')
}
validateFields() is a function of Vue's version of Ant Design, and validates my form input. More about it here, maybe it will help inform any of you.
So what exactly am I doing wrong here?

Async await exists to simplify the syntax for working with promises. If you aren't using promises, then async/await gains you nothing.
It looks like validateFields is using callbacks instead of promises, so you'll need to create your own promise around it. For example:
async register () {
try {
const values = await new Promise((resolve, reject) => {
this.form.validateFields((err, values) => {
if (err) {
reject (err);
} else {
resolve(values);
}
})
})
console.log('got values', values);
} catch (error) {
console.log('caught an error', error);
}
}

Related

How exactly works this async\await code in my Angular application? Why the colled method doesn't explicitly return a Promise?

I am not so into RxJS and I am finding some problems to understand this piece of code retrieved into an Angular project on which I am working.
First of all into a component TypeScript code I have this method:
async post(): Promise<void> {
this.submitted.next(true);
try {
await this.setAddress();
this.activeModal.close();
} catch (e) {
console.error('Storage upload error', e);
this.submitted.next(false);
}
}
As you can see this method have async prefix because into the try block it contains these 2 rows:
await this.setAddress();
this.activeModal.close();
from what I have understand (please correct me if I am doing wrong assertion) basically the await in front of this.setAddress() it means: await that this method call end, when it is completed executed the following operation (that in this case close a modal window).
From what I have understand it replave the then() method handling a Promise resolution. Is it correct or not?
So my doubt is: have my setAddress() method return a Promise? In my specific case setAddress() method is used to call a service method saving some data on the database and have this code:
async setAddress(): Promise<void> {
try {
const c: Address = {
companyName:this.addressFormGroup.get('companyName').value,
street: this.addressFormGroup.get('street').value,
city: this.addressFormGroup.get('city').value,
zipCode: this.addressFormGroup.get('zipCode').value,
notes: this.addressFormGroup.get('notes').value,
};
//save/update record
await this.userService.setUserAdresss(this.currentUserUID,this.addressType,c);
this.success = true;
if (!this.isEditMode) {
this.addressFormGroup.reset();
}
} catch (e) {
console.error(e);
} finally {
this.submitted.next(false);
}
}
And here I have a lot of doubts on how it works...ok the method signature:
async setAddress(): Promise<void> {
seems to return a Promise (why ? what it means?). But where is it effectivelly returning a Promise? In the code of this method I can't find that it is returning a Promise nowhere. It seems to me that it is returning nothing because it is not containing the return statement !!!
My only interpretation is the following one (but it is my idea and probably totally wrong): also if it is not explicitly returning nothing it have a Promise as method returned type. So it means that at the end of the method execution TypeScript automatically return an "empty" Promise that simply means: "method execution completed".
I am absolutly not sure, this is the only explaination that I can give to this code.
How it wxactly works?
Your assumptions are correct.
A function declared with the async keyword will return a Promise that is completed when the function has finished its execution.
The await keyword is equivalent to using the then method with the remaining lines of codes as the callback function.
Using try/catch/finally arround an await is equivalent to using the catch/finally method on the promise.
This is your code written with promises instead of async/await :
post(): Promise<void> {
this.submitted.next(true);
return this.setAddress()
.then(() => this.activeModal.close())
.catch((e) => {
console.error('Storage upload error', e);
this.submitted.next(false);
});
}
setAddress(): Promise<void> {
const c: Address = {
companyName:this.addressFormGroup.get('companyName').value,
street: this.addressFormGroup.get('street').value,
city: this.addressFormGroup.get('city').value,
zipCode: this.addressFormGroup.get('zipCode').value,
notes: this.addressFormGroup.get('notes').value,
};
//save/update record
return this.userService.setUserAdresss(this.currentUserUID,this.addressType,c)
.then(() => {
this.success = true;
if (!this.isEditMode) {
this.addressFormGroup.reset();
}
})
.catch((e) => console.error(e))
.finally(() => this.submitted.next(false));;
}

Mongoose pass data out of withTransaction helper

Introduction
Hey there,
I am trying to pass out data from the mongoose withTransaction callback. Right now, I am using the following code which implements callbacks:
const transactionSession = await mongoose.startSession()
await transactionSession.withTransaction(async (tSession) => {
try {
// MARK Transaction writes & reads removed for brevity
console.log("Successfully performed transaction!")
cb(null, "Any test data")
return Promise.resolve()
} catch (error) {
console.log("Transaction aborted due to error:", error)
cb(error)
return Promise.reject()
}
})
} catch (error) {
console.log(error)
return cb(error)
}
A more detailed snippet of the withTransaction helper in use can be found here.
A link to the official Mongoose documentation regarding the withTransaction helper can be found here.
At the moment, I am using a callback to pass out data from the withTransactioncallback:
cb(null, "Any test data")
However, the problem is that naturally the callback is executed first, before the Promise.resolve() is returned. This means, that (in my case) a success response is sent back to the client before any necessary database writes are committed:
// this is executed first - the callback will send back a response to the client
cb(null, "Any test data")
// only now, after the response already got sent to the client, the transaction is committed.
return Promise.resolve()
Why I think this is a problem:
Honestly, I am not sure. It just doesn't feel right to send back a success-response to the client, if there hasn't been any database write at that time. Does anybody know the appropriate way to deal with this specific use-case?
I thought about passing data out of the withTransaction helper using something like this:
const transactionResult = await transactionSession.withTransaction({...})
I've tried it, and the response is a CommandResult of MongoDB, which does not include any of the data I included in the resolved promise.
Summary
Is it a problem, if a success response is sent back to the client before the transaction is committed? If so, what is the appropriate way to pass out data from the withTransaction helper and thereby committing the transaction before sending back a response?
I would be thankful for any advice I get.
It looks like there is some confusion here as to how to correctly use Promises, on several levels.
Callback and Promise are being used incorrectly
If the function is supposed to accept a callback, don't return a Promise. If the function is supposed to return a Promise, use the callback given by the Promise:
const transactionSession = await mongoose.startSession()
await transactionSession.withTransaction( (tSession) => {
return new Promise( (resolve, reject) => {
//using Node-style callback
doSomethingAsync( (err, testData) => {
if(err) {
reject(err);
} else {
resolve(testData); //this is the equivalent of cb(null, "Any test data")
}
});
})
Let's look at this in more detail:
return new Promise( (resolve, reject) => { This creates a new Promise, and the Promise is giving you two callbacks to use. resolve is a callback to indicate success. You pass it the object you'd like to return. Note that I've removed the async keyword (more on this later).
For example:
const a = new Promise( (resolve, reject) => resolve(5) );
a.then( (result) => result == 5 ); //true
(err, testData) => { This function is used to map the Node-style cb(err, result) to the Promise's callbacks.
Try/catch are being used incorrectly.
Try/catch can only be used for synchronous statements. Let's compare a synchronous call, a Node-style (i.e. cb(err, result)) asynchronous callback, a Promise, and using await:
Synchronous:
try {
let a = doSomethingSync();
} catch(err) {
handle(err);
}
Async:
doSomethingAsync( (err, result) => {
if (err) {
handle(err);
} else {
let a = result;
}
});
Promise:
doSomethingPromisified()
.then( (result) => {
let a = result;
})
.catch( (err) => {
handle(err);
});
Await. Await can be used with any function that returns a Promise, and lets you handle the code as if it were synchronous:
try {
let a = await doSomethingPromisified();
} catch(err) {
handle(err);
}
Additional Info
Promise.resolve()
Promise.resolve() creates a new Promise and resolves that Promise with an undefined value. This is shorthand for:
new Promise( (resolve, reject) => resolve(undefined) );
The callback equivalent of this would be:
cb(err, undefined);
async
async goes with await. If you are using await in a function, that function must be declared to be async.
Just as await unwraps a Promise (resolve into a value, and reject into an exception), async wraps code into a Promise. A return value statement gets translated into Promise.resolve(value), and a thrown exception throw e gets translated into Promise.reject(e).
Consider the following code
async () => {
return doSomethingSync();
}
The code above is equivalent to this:
() => {
const p = new Promise(resolve, reject);
try {
const value = doSomethingSync();
p.resolve(value);
} catch(e) {
p.reject(e);
}
return p;
}
If you call either of the above functions without await, you will get back a Promise. If you await either of them, you will be returned a value, or an exception will be thrown.

Is logic-heavy Promise chaining "callback hell"?

I launch my first promise and wait for it (this is the very high-level process of my system, it's where I chain everything together):
install_demo_component.then(), then, I need to handle the response from that:
install_demo_component.then(response => {
if(response.failed) {
undo_changes_we_did_from_install.then(response => {
if(response.failed) {
completely_uninstall_demo_component.then(response => {
if(response.failed) {
throw BIG_ERROR;
}
}).catch(error => {
});
}
}).catch(error => {
});
}
}).catch(error => {
});
But I can't avoid it. Nor can I make it prettier, me thinks. The way these promises are structured to wait for each other or start a new promise chain if response.failed arises is essential to how my system works, it's related to the business logic.
I think a lot of people would see this as callback hell and although I think callback hell in essence is something else, I agree it doesn't look nice.
If it really is callback hell, what's the alternative?
Original Answer
That isn't really promise chaining. To chain promises, you need to return a promise from the .then() method. I would rewrite your code like this:
install_demo_component.then(response => {
if(response.failed) return undo_changes_we_did_from_install
return Promise.reject();
}).then(response => {
if(response.failed) return completely_uninstall_demo_component
return Promise.reject();
}).then(response => {
if(response.failed) throw BIG_ERROR;
return Promise.reject();
}).catch(error => {
//You only need one catch when promise chaining.
});
returning Promise.reject(); will break out of the promise chain and jump to .catch()
Click here for more info on promise chaining.
Error handling with promises
My original response was how to convert your code to a promise chain without changing it too much. I believe that's the best way to help people understand what was changed. However, I do have a suggestion on how you can use promises to their fullest.
Instead of checking for response.failed, you can have your asynchronous functions such as undo_changes_we_did_from_install reject on failure. This will remove the need for all of the conditional statements.
//The function below will either resolve() or reject()
install_demo_component.catch(response => { //Notice only .catch() is used.
return undo_changes_we_did_from_install
}).catch(response => {
return completely_uninstall_demo_component
}).catch(response => {
throw BIG_ERROR;
};
You see .catch() is already invoked conditionally, the condition is if the previous promise rejected instead of resolved. And you see that the .catch() method can also be chained.
For a short article on this go here.
To avoid callback hell, all you have to do is run those install test inside of an async function. This can be done inside of an IIFE:
// returns a Promise
function install_demo_component() {
return new Promise((resolve, reject) => {
resolve({
failed: false,
func: 'install_demo_component'
});
});
}
// returns a Promise
function undo_changes_we_did_from_install() {
return new Promise((resolve, reject) => {
resolve({
failed: true,
func: 'undo_changes_we_did_from_install'
});
});
}
// returns a Promise
function completely_uninstall_demo_component() {
return new Promise((resolve, reject) => {
resolve({
failed: true,
func: 'completely_uninstall_demo_component'
});
});
}
// run install tests inside of async IIFE (Immediately Invoked Function Expression)
(async function() {
// try/catch is used instead of the .catch() method when using async/await
try {
// using the new `...` from ES6 inside of an obj litteral, properties can be accessed from the function
if({ ...await install_demo_component() }.failed) {
console.log('install failed');
} else if({ ...await undo_changes_we_did_from_install()}.failed ) {
console.log('undo install changes failed');
} else if({ ...await completely_uninstall_demo_component() }.failed) {
console.log('completely uninstall demo component failed!!!!, What do we do?!!!!!');
}
} catch(err) {
console.log(err);
}
}());
EDIT
Made the if/else logic make more sense based off of the order of actions needing to take place:
// run install tests inside of async IIFE (Immediately Invoked Function Expression)
(async function() {
// try/catch is used instead of the .catch() method when using async/await
try {
// using the new `...` from ES6 inside of an obj litteral, properties can be accessed from the function
if(!{ ...await install_demo_component() }.failed) {
console.log('install succeed');
} else if(!{ ...await undo_changes_we_did_from_install() }.failed) {
console.log('undo install changes succeed');
} else if(!{ ...await completely_uninstall_demo_component() }.failed) {
console.log('completely uninstall demo component succeed');
} else {
console.log('Everything failed and now this sucker is gaana blow');
}
} catch(err) {
console.log(err);
}
async await does wonders to get rid of all the trailing });
async function ur_func() {
let response = await install_demo_component;
if (response.failed) {
let undo_response = await undo_changes_we_did_from_install
if (undo_response.failed) {
let uninstall_response = completely_uninstall_demo_component;
if (uninstall_response) {
throw BIG_ERROR;
}
}
}
}
ur_func();
You could also use early returns to get rid of the indentation, but that's more a matter of preference.

How to convert callback code to async/await? [duplicate]

This question already has answers here:
Nodejs Mongoose - how to avoid callback hell?
(3 answers)
Closed 5 years ago.
I am working with Mongoose ODM for MongoDB in a node express-based web application which provides its API in a callback fashion. This fashion creates a callback hell to populate documents.
Edit 1: I have added the current working code.
User.remove({}, (err) => {
if (err) return console.error(err)
let admin = new User({
email: 'admin#dap.com',
password: 'dapdap'
})
admin.save((err, admin) => {
if (err) return console.error(err)
console.info('Success')
mongoose.disconnect()
})
})
Is there an elegant way to convert the callbacks into async/await fashion in javascript in general? And in mongoose environment?
Would you be able to configure mongoose to use bluebird?
mongoose.Promise = require('bluebird');
Then you could get fancy and do:
User.remove().then(() => {
let admin = new User({ ... });
admin.save().then((admin) => {
}).catch((err) => {
//Saving admin error handling
});
}).catch((err) {
//User remove error handling
});
The same code you wrote above can be written as a promise. From the original docs,
"Mongoose queries are not promises. However, they do have a .then() function for yield and async/await. If you need a fully-fledged promise, use the .exec() function."
Thus you can use the async await pattern as follows,
async function removeUser () {
// Surround the following call with a try catch
await User.remove({});
}
EDIT:
One answer here says about plugging in blue bird promises. While this is a good approach, it is optional. You can do the same without plugging in your own Promise library.
You can use a Promise:
const userRemovePromise = new Promise((resolve, reject) => {
User.remove({}, (err) => {
if (err) reject();
let admin = new User({
email: 'admin#dap.com',
password: 'dapdap'
})
admin.save((err, admin) => {
if (err) reject();
resolve();
})
});
});
userRemovePromise.then(function(){
mongoose.disconnect();
});
As mongoose's default promise library mpromise is now deprecated so you can plugin your own promise library to make mongoose's operations work with await/async.
Mongoose queries are not promises. However, they do have a .then() function for yield and async/await. If you need a fully-fledged promise, use the .exec() function.
mongoose.Promise = require('bluebird'); bluebird promise
// mongoose.Promise = global.Promise; // default js promise - 4x- time slower than bluebird
async function removeUser () {
try {
await User.remove({}).exec();
let admin = new User({
email: 'admin#dap.com',
password: 'dapdap'
})
await admin.save();
} catch (e) {
console.log(e);
}
}
Official Doc: mongoosejs.com/docs/promises.html
Recent versions of Mongoose returns a Promise as well as providing the regular callback-style pattern. Since async functions are syntatic sugar over Promises, you can await calls to Mongoose methods.
async function clearUsers () {
try {
await User.remove({})
let admin = new User({
email: 'admin#dap.com',
password: 'dapdap'
})
await admin.save()
console.info('Success')
mongoose.disconnect()
} catch (e) {
console.error(e)
}
}
Some things to keep in mind:
async functions always returns a Promise. If you don't return anything from an async function, it will still return a Promise that resolves when execution of that function finishes, but will not resolve with any value.
try/catch in an async function works the same as for regular, synchronous code. If any function call in the try body throw or return a Promise that rejects, execution stops right on that line and proceeds to the catch body.
Rejected promises "trickle" up the function call chain. This means that the top-most callee can handle errors. See the below example:
This is an anti-pattern that should be avoided unless you absolutely need to handle an error in a specific function, possibly to provide a return value from a different source:
async function fn1 () {
throw new Error("Something went wrong")
}
async function fn2 () {
try {
await fn1()
} catch (e) {
throw e
}
}
async function fn3 () {
try {
await fn2()
} catch (e) {
throw e
}
}
async function run () {
try {
await fn3()
} catch (e) {
console.error(e)
}
}
The above could rather be implemented like the below and still catch the error, not resulting in a runtime panic/crash:
async function fn1 () {
throw new Error("Something went wrong")
}
function fn2 () {
return fn1()
}
function fn3 () {
return fn2()
}
async function run () {
try {
await fn3()
} catch (e) {
console.error(e)
}
}
There are multiple ways to write the above code that will all be valid, so I recommend that you explore these.
Keeping the above examples in mind, your function clearUsers() could then be rewritten to this:
async function clearUsers () {
await User.remove({})
let admin = new User({
email: 'admin#dap.com',
password: 'dapdap'
})
await admin.save()
mongoose.disconnect()
}
And then possibly called in two different ways;
By interacting with the Promise returned directly:
clearUsers()
.then(() => {
console.log('Success')
})
.catch((e) => {
console.error(e)
})
Or from within another async function:
(async function () {
try {
await clearUsers()
console.log('Success')
} catch (e) {
console.error(e)
}
})()
If any function call within the clearUsers() function throws, for example await admin.save(), execution will stop on that line and return a rejected Promise which will be caught in the corresponding catch blocks in both variants.
Using Bluebird is a good option to handle callbacks in promises, It makes it easier to understand to an extent.
However I would recommend you to try
Async.js
It is built particularly for managing asynchronous nature of javascript.
This particular function of the library would do exactly what you want.

Using mongoose promises with async/await

I'm trying to get the hang of using Mongoose promises with the async/await functionality of Node.js. When my function printEmployees is called I want to save the list of employees which are queried by the orderEmployees function. While, the console.log statement inside orderEmployees returns the expected query, the console.log inside of printEmployees returns undefined, suggesting that I'm not returning the promise correctly.
I'm new to promises so entirely possible that I'm not correctly understanding the paradigm... any help is much appreciated.
printEmployees: async(company) => {
var employees = await self.orderEmployees(company);
// SECOND CONSOLE.LOG
console.log(employees);
},
orderEmployees: (companyID) => {
User.find({company:companyID})
.exec()
.then((employees) => {
// FIRST CONSOLE.LOG
console.log(employees);
return employees;
})
.catch((err) => {
return 'error occured';
});
},
In order to make orderEmployees behave like async functions, you have to return the resulting promise. There are two rules to follow when using promises without async/await keywords:
A function is asynchronous if it returns a Promise
If you have a promise (for example returned by an async function) you must either call .then on it or return it.
When you are using async/await then you must await on promises you obtain.
This said you will notice that you do not return the promise generated inside orderEmployees. Easy to fix, but its also easy to rewrite that function to async too.
orderEmployees: (companyID) => {
return User.find({company:companyID}) // Notice the return here
.exec()
.then((employees) => {
// FIRST CONSOLE.LOG
console.log(employees);
return employees;
})
.catch((err) => {
return 'error occured';
});
},
or
orderEmployees: async(companyID) => {
try {
const employees = await User.find({company:companyID}).exec();
console.log(employees);
return employees;
} catch (err) {
return 'error occured';
}
},
PS: the error handling is somewhat flawed here. We usually do not handle errors by returning an error string from a function. It is better to have the error propagate in this case, and handle it from some top-level, UI code.
You need to return your Promise.
Currently, you are awaiting on a function that returns undefined.
await only actually "waits" for the value if it's used with a Promise.
Always keep in mind that you can only await Promises or async functions, which implicitly return a Promise1.
orderEmployees: (companyID) => {
return User.find({ company:companyID }).exec()
}
Also really important, you should throw instead of return in your .catch handler. Returning from within a .catch handler will cause the promise chain to trigger it's .then instead of it's .catch thus breaking the error handling chain.
Better yet, don't include .catch at all and let the the actual error bubble up the promise chain, instead of overriding it with your own non-descriptive 'error occured' message.
Error conditions should throw the error, not return it.
1 You can also await non-Promises, but only for values that are evaluated synchronously.
You are not returning a Promise from orderEmployees.
printEmployees: async(company) => {
var employees = await self.orderEmployees(company);
// SECOND CONSOLE.LOG
console.log(employees);
},
orderEmployees: (companyID) => {
return User.find({company:companyID})
.exec()
.then((employees) => {
// FIRST CONSOLE.LOG
console.log(employees);
return employees;
})
.catch((err) => {
return 'error occured';
});
},
You need to return a Promise from orderEmployees
orderEmployees: companyId => User.find({ companyId }).exec()
If you want to do some error handling or pre-processing before you return then you can keep your code as is but just remember to return the result (promises are chainable).
if you're going to use async/await then it works like this.
await in front of the function that returns a promise.
async in front of the wrapping function.
wrap the function body inside try/catch block.
Please have a look on this function, it is a middleware
before i execute a specific route in express.
const validateUserInDB = async (req, res, next) => {
try {
const user = await UserModel.findById(req.user._id);
if (!user) return res.status(401).json({ message: "Unauthorized." });
req.user = user;
return next();
} catch (error) {
return res.status(500).json({ message: "Internal server error." })
}
}
The code after await is waiting the promise to be resolved.
Catch block catches any error happened inside the try block even if the error that is triggered by catch method comes from awaiting promise.

Categories