When programming, the “asynchronous” keyword in javascript will definitely need to be known and will certainly be important if you want to work better with it. However, understanding and using it properly is sometimes a problem.
So in this article, I would like to introduce a few basic things, which can help you solve some difficulties when approaching asynchronous programming.
1. Basics
First of all, to solve the “asynchronous” problem, you need to understand and know three common solutions: Callback, promise, async / await.
Promise solves callback issues, async / await solves promises for promises. However, depending on the case, I will use which solution is reasonable. Let’s take a look at the following examples to better understand the 3 options above.
2. Example between callback and promise
Consider the example:
This is a code for reading files
1 2 3 4 5 6 7 8 9 10 | const fs = require('fs'); fs.readFile('/path/to/file', (err, file) => { if(err) { // handle the error } else { // do something with the file } }); |
The fs.readFile
function takes the path of a file and returns the argument. The callback function returns an error or null when read incorrectly and returns a file upon successful reading.
When switching to Promise:
1 2 3 4 5 6 7 8 | fs.readFile('/path/to/file') .then(file => { // do something with the file }) .catch(err => { // handle the error }); |
Or write wrap in a Promise:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | const readFileAsync = path => { return new Promise((resolve, reject) => { fs.readFile(path, (err, file) => { return err ? reject(err) : resolve(file); }); }); }; // usage readFileAsync('/path/to/file') .then(file => { // do something with the file }) .catch(err => { // handle the error }); |
Now the readFileAsync
function takes a file path then executes readFile and returns a promise. If the readFile
error function receives an error message via reject
, and if successful, it receives the file information via resolve
.
We notice here, promises are also written based on callbacks. This is also a way of writing promises based on callbacks.
3. Example between promise and async / await
1. Another example with FileReader
:
Read the browser file and turn it into an ArrayBuffer
Writing with callback
1 2 3 4 5 6 7 8 9 10 11 | const toArrayBuffer = blob => { const reader = new FileReader(); reader.onload = e => { const buffer = e.target.result; }; reader.onerror = e => { // handle the error }; reader.readAsArrayBuffer(blob); }; |
How to write with Promise:
1 2 3 4 5 6 7 8 9 | const toArrayBuffer = blob => { const reader = new FileReader(); return new Promise((resolve, reject) => { reader.onload = e => resolve(e.target.result); reader.onerror = e => reject(e.target.error); reader.readAsArrayBuffer(blob); }); }; |
Pretty clear and easy to understand, because quite similar to the previous example, however, the promise writing seems shorter and clearer.
However, let’s consider more examples working with promises and how to solve using async / await.
If you work with promises a lot, you will probably have overlapping problems like:
1 2 3 4 5 6 7 8 | const db = openDatabase(); db.getUser(id) .then(user => db.getOrders(user)) .then(orders => db.getProducts(orders[0])) .then(products => { // cannot access orders here! }) |
3 actions: getUser, getOrders, getOrders perform one after the other. However, in the above code, after taking the getOrders
action, the orders of the following then cannot be accessed, because it is within the Promise callback scope previously returned from db.getOrders()
.
So the solution is:
1 2 3 4 5 6 7 8 9 10 11 12 | const db = openDatabase(); let orders; // initialized in outer scope db.getUser(id) .then(user => db.getOrders(user)) .then(response => { orders = response; // orders is assigned here return db.getProducts(orders[0]); }) .then(products => { // orders is now accessible here! }) |
Now it is okay, however, if under the product there are a series of then again, then there are a series of initialized variables (haiz). If instead we use async / await then:
1 2 3 4 5 6 7 8 9 10 11 | const db = openDatabase(); const getUserData = async id => { const user = await db.getUser(id); const orders = await db.getOrders(user); const products = await db.getProducts(orders[0]); return products; }; //use getUserData(123); |
Wow, more concise and we don’t have to create more variables after each promise, all variables in getUserData
have the same scope and are accessible.
In particular, you can write:
1 2 3 4 | const getProducts = async () => { const products = await getUserData(123); }; |
Or combine promises and async / await:
1 2 3 4 5 | getUserData(123) .then(products => { // use products }) |
Because, async always returns a promise.
2. Another example when dealing with multiple api calls at once.
Send email to 100 customers / 1 time
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | const sendMailForUsers = async (users) => { const usersLength = users.length for (let i = 0; i < usersLength; i += 100) { const requests = users.slice(i, i + 100).map((user) => { // Mỗi đợt 100 email. và xử lý chúng return triggerMailForUser(user) // Async function to send the mail. .catch(e => console.log(`Error in sending email for ${user} - ${e}`)) }) // requests sẽ có 100 hoặc ít hơn các promise đang chờ xử lý. // Promise.all sẽ đợi cho đến khi tất cả các promise //đã được giải quyết và sau đó thực hiện 100 lần tiếp theo. await Promise.all(requests) .catch(e => console.log(`Error in sending email for the batch ${i} - ${e}`)) // Catch the error. } } sendMailForUsers(userLists) |
However, with promise.all, it is impossible to know exactly which customers were sent an error mail, but only identified in the batch that sent the error mail.
And this is the power of async / await
1 2 3 4 5 6 7 8 | const sendMailForUsers = async (users) => { for(item of users){ item.response = await triggerMailForUser(item) } } sendMailForUsers(userLists) |
Now, each item will have a response, to know if the item has successfully sent or not.
4. Conclusion
Above are some basic examples when working with callbacks, promises, async / await. However, when used, there are many other problems encountered, (because of javascript sida ), but this is also a basic knowledge for us to get closer to “asynchronous”, hoping to help you.
Thank you for reading, I hope there are many suggestions for the article to be better.
Reference source: https://medium.com/swlh/what-you-need-to-know-about-asynchronous-programming-in-javascript-894f90a97941 https://topdev.vn/blog/promise-all-la -gi /