I have this scenario:
app.get('/', async function (request, response)
{
await foo(DbConnection).then((result) => console.log("response ready"));
})
let foo = async function (DbConnection)
{
const myQuery = "SELECT * FROM my_table";
DbConnection.query(myQuery, async function(err, results) {
console.log("query ready");
await bar(results).then((response) => console.log("bar done"))
})
return; // go back to app.get() to send stuff to client
}
let bar = async function (results)
{
console.log("inside bar");
await ThirdPartyAPI(result).then((value)) => return value);
}
Briefly:
I receive GET request from client
I call foo() which query data base and apply functions on results
I process results using third party API which takes time to finish
I send final results back to client
I am expecting to see:
query ready -> inside bar -> bar done -> response ready
But I instead see:
response ready -> query ready -> inside bar -> bar done
Client is receiving undefined since nothing was ready when response.send()
What could be the issue?
The main problem in your code is that you're mixing async/await syntax and callbacks. Use async function only when you want to do something with the result of async call inside this function.
Also that, specific to your case, you need to promisify connection.query() - create a promise. See below the correct pattern with a working code:
app.get('/', async (request, response) => {
// waiting for the result of foo
let result = await foo();
console.log("response ready");
});
let foo = async () => {
const myQuery = "SELECT * FROM my_table";
// getting the result of the query
let results = await new Promise((resolve, reject) => connection.query(myQuery, (err, results) => {
if (err) {
reject(err)
} else {
resolve(results);
}
}));
console.log("query ready");
// call bar and waiting the result
let res = await bar(results);
console.log("bar done");
// return resolved promise
return res;
}
let bar = (results) => {
console.log("inside bar");
// just return a promise, we don't need the result here
return ThirdPartyAPI(result);
}
Explanation about using async/await. The power of async/await is that it lets you write asynchronous code using synchronous language constructs. But that doesn't mean that all functions should be marked as async. async wraps a function into a promise, if the function executes synchronous code, this wrapping can cause an error (you expect the string for example, but function returns a resolved promise). Another example when the function executes async code, but doesn't use intermediate result, in this case it's enough just return the result of async call.
You should use async/await when:
Let's consider some examples:
1.sync function, validates userId. validateParams returns true/false, validateParams2 throws an error if userId is undefined. validateParams3 returns a promise, and can be used for creating a promise chain: validateParams3().then(...).
let validateParams = (userId) => {
return userId;
}
let validateParams2 = (userId) => {
if (!userId) {
throw new Error('userId is undefined');
}
}
let validateParams3 = (userId) => {
if (!userId) {
return Promise.reject(new Error('userId is undefined'));
}
return Promise.resolve();
}
2.async function, doesn't use intermediate result. getUser returns a pending promise (when userId is valid) or fulfilled promise (when userId is undefined). It should return a promise, because getUser can be used for starting a promise chain: createUser().then(...). getUser2 and getUser3 do the same thing, return a promise. The promise is requred here to avoid getting unhandledError error, because validateParams2 can throw an error. getUser2 makred as async (to create a promise automatically), and getUser3 returns a promise.
let getUser = (userId) => {
if (validateParams(userId)) {
return db.getUserById(userId);
}
return Promise.resolve();
}
let getUser2 = async (userId) => {
validateParams2(userId);
return db.getUserById(userId);
}
let getUser3 = (userId) => {
return Promise
.resolve(userId)
.then(validateParams2)
.then(() => db.getUserById(userId);
}
3.async function, uses an intermediate result:
let updateUser = async (userId, userData) => {
let user = await getUser(userId);
_.extend(user, userData);
return db.saveUser(user);
}
The functions above can be used this way:
// with async
let fn = async (userId, userData) => {
try {
let user = await updateUser(userId, userData);
console.log(user);
}
catch (err) {
console.log(err);
}
}
// without async
let fn2 = (userId, userData) => {
updateUser(userId, userData)
.then(user => console.log(user))
.catch(err => console.log(err))
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With