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