0.4.6.1: Async Await
Learning Objectives
Async-await can be cleaner syntax but is functionally the same as
.then
syntaxHow to use async-await syntax to manage promise control flow in lieu of
.then
Try-catch syntax provides us
.catch
functionality with async-await syntax
Introduction
.then
syntax:
// myFunc returns the return value of myFunc, currently undefined
const myFunc = () => {
axios.get("foobar.com").then((data) => {
// Do something with data after response received
console.log(data);
});
};
Async-await syntax:
// myFunc returns a promise due to the async keyword
// The promise resolves to the return value of myFunc
const myFunc = async () => {
const data = await axios.get("foobar.com");
// Do something with data after response received
console.log(data);
};
Async-await syntax allows us to write asynchronous JavaScript in a synchronous manner, like in the example above. This can result in cleaner code, but does not add new functionality. Rocket does not have a strong preference whether to use async-await or .then
syntax.
async
specifies a given function is asynchronous and returns a promise, and await
will wait for a given promise to resolve before proceeding to the next line. async
and await
keywords must be used together; it is not meaningful to use async
without await
, and it is invalid to use await
without async
.
Example: Async-await with pg
pg
Async-await syntax is generally preferred due to its increased readability compared with .then
syntax.
.then
syntax:
app.get('/users/:id', (request, response) => {
const { id } = request.params;
pool.query('SELECT * FROM users WHERE id = $1', [id]).then((result) => {
const { rows } = result;
response.send(rows);
};
});
Async-await syntax:
app.get("/users/:id", async (request, response) => {
const { id } = request.params;
const result = await pool.query("SELECT * FROM users WHERE id = $1", [id]);
const { rows } = result;
response.send(rows);
});
Here are the official docs on how to use pg
with async-await syntax in Express.
Example: Catch errors with async-await
Try-catch syntax allows us to catch errors with async-await syntax in the same way we would catch errors with .then
and .catch
syntax.
.then
syntax:
const getRecipes = () => {
// client is a Client instance from the Node pg library
client
// .query returns a promise
.query("SELECT * from recipes WHERE category=vegan")
.then((recipes) => {
// Render the lovely vegan recipes
})
// The .catch block will trigger on error in either .query or .then block
.catch((error) => {
console.error(error);
// Handle the error gracefully, e.g. render 404 page instead of crashing app
});
};
Async-await syntax:
const getRecipes = async () => {
try {
// client is a Client instance from the Node pg library
const recipes = await client.query(
"SELECT * from recipes WHERE category=vegan"
);
// Render the lovely vegan recipes
} catch (error) {
console.error(error);
// Handle the error gracefully, e.g. render 404 page instead of crashing app
}
};
Similar to .catch
syntax, when there is an error in a try
block, e.g. a request to a nonexistent URL, the error will cause our program will crash unless we catch that error in a catch
block. Try-catch syntax is not directly related to promises, but is commonly used with async-await promise syntax.
Example: Async-await does not pause programs, only code in current function
The following code executes "Before" and "After" console.log
s before "Recipes".
const getRecipes = async () => {
try {
// client is a Client instance from the Node pg library
const recipes = await client.query(
"SELECT * from recipes WHERE category=vegan"
);
// Render the lovely vegan recipes
console.log("Recipes");
} catch (error) {
console.error(e);
// Handle the error gracefully, e.g. render 404 page instead of crashing app
}
};
console.log("Before");
getRecipes();
console.log("After");
Output
Before
After
Recipes
getRecipes
will return before its logic has completed because it is an async
function that contains an asynchronous client.query
. async
wraps getRecipes
in a promise that returns immediately but resolves only when getRecipes
logic is complete. Since there is no .then
or await
on the getRecipes()
function call, the "After" console.log
runs before getRecipes
has resolved.
Example: Async-await works with all promises, including the promise returned by Promise.all
Promise.all
We can use async-await with Promise.all
to retrieve unrelated data concurrently with syntax that reads sequentially.
.then
syntax:
const getData = () => {
const results = Promise.all([
pool.query("SELECT * FROM recipes"),
pool.query("SELECT * FROM categories"),
pool.query("SELECT * FROM users"),
// results is an array of results whose elements correspond
// to the elements in the Promise.all parameter array
]).then((results) => {
const [recipes, categories, users] = results;
// Do something with recipes, categories and users
});
};
Async-await syntax:
const getData = async () => {
// results is an array of results whose elements correspond
// to the elements in the Promise.all parameter array
const results = await Promise.all([
pool.query("SELECT * FROM recipes"),
pool.query("SELECT * FROM categories"),
pool.query("SELECT * FROM users"),
]);
const [recipes, categories, users] = results;
// Do something with recipes, categories and users
};