Promise.all javascript giúp tôi xử lý performance thế nào?

Nội dung bài viết

Đầu tiên nếu bạn chưa biết về Promise.all thì vui lòng qua đọc qua những bài viết trước đó "Tổng hợp bài viết về Promise, Async/await trong javascript"
Tất cả những gì bạn biết về Promise.all là gì? Bạn đã dùng Promise.all khi nào? Performance khi dùng Promise.all thế nào? Những câu hỏi này, chúng ta sẽ cùng thảo luận trong bài viết này. Hy vọng, bạn có thể làm nhiều thứ hơn khi đọc xong bài viết này. 

Promise.all là gì?

Khi bạn đang tìm kiếm về Promise.all() thì các phiên bản hiện tại đã có thêm những method bổ sung thêm như Promise.allSettled. Vì thế các bạn cũng nên tìm hiểu nhằm hiểu hơn về Promise trong javascript.

Xem thêm: Promise.race vs. Promise.any And Promise.all vs. Promise.allSettled

Promise trong JavaScript là một trong những API mạnh mẽ giúp chúng ta thực hiện các Async operations. Promise.all đưa Async operations lên một trình mới vì nó giúp chúng ta controls được một group Promises. Promise.all giúp chúng ta cải thiện được performance về các yêu cầu như send mail hàng loạt.

Cú pháp cơ bản:

Promise.all([Promise1, Promise2, Promise3])
 .then(result) => {
   console.log(result)
 })
 .catch(error => console.log(`Error in promises ${error}`))

Trong đó Promise1, Promise2, Promise3 là những promise và Promise.all giúp giải quyết khi tất cả promises được giải quyết hoặc bất kỳ một trong số chúng bị reject. Các bạn có thể lướt qua bài viết Promise.all javascript giúp tôi xử lý performance thế nào? sẽ hiểu hơn về một pha lập trình đầy kinh nghiệm của những lập trình lâu năm

 Một vài ví dụ cho thấy Promise.all lợi hại thế nào 

Ví dụ 1 - Promise.all và map()

//promise sẽ resolves với một thời gian là t
const timeOut = (t) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`Completed in ${t}`)
    }, t)
  })
}

//tương ứng với t
const durations = [1000, 2000, 3000]

const promises = [];// một array chứa tất cả các promise

durations.map((duration) => {
  // Có hai sự kiện xảy ra ở đây
  // 1. Chúng ta gọi async function (timeout())
  // 2. Chúng ta push những promise timeout() vào một array để promise.all giải quyết
  promises.push(timeOut(duration)) 
})

console.log(promises); //output có dạng [ Promise { "pending" }, Promise { "pending" } ...]

//Promise.all sẽ đợi cho đến khi tất cả các promises được giải quyết 
Promise.all(promises)
.then(response => console.log(response)) 

//["Completed in 1000", "Completed in 2000", "Completed in 3000"]

Notes: Promise.all sẽ trả về các value tương ứng với thứ tự promises được đưa vào trong array.

Bạn đã dùng Promise.all khi nào? 

Có rất nhiều trường hợp để sử dụng Promise.all nhưng qua các dự án thì chúng tôi thường thấy đa số là xảy ra 2 trường hợp sau đây (được dùng nhiều và hiệu quả) 

# Send Email

Giả sử bạn là một người Marketing và bạn muốn send email tới nhiều khách hàng có thể là hàng ngàn email. Thông thường thì chúng ta sẽ làm điều gì đó như thế này:

for (let i=0;i<50000; i += 1) {
 sendMailForUser(user[i]) // Async operation to send a email
}

Ví dụ trên là đơn giản. Nhưng nó không tốt nếu nói về performance. Có thể hơn stack sẽ quá nặng tại một thời điểm nào đó và , JavaScript sẽ có một số lượng lớn kết nối HTTP có thể giết chết máy chủ của bạn. Vậy một cách nào an toàn hơn. Một cách tiếp cận thực hiện đơn giản sẽ được thực hiện theo từng đợt. 

Lấy 100 (ví dụ) email đầu tiên, phát tín hiệu gửi đi và đợi cho đến khi tất cả các kết nối HTTP được đóng lại. Và sau đó lấy đợt tiếp theo và cứ như thế. Nó tránh được việc server quá tải.

Ví dụ :

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)

Thật ra bạn nào mà chưa hiểu ví dụ vì sao lại sử dụng slice thì nên xem lại mình đi nha. Và đọc thêm Hiểu rõ splice, split và slice trong javascrip để hiểu rõ hơn.
Giải thích cho những devjs nào đang ngáo đá đó là sử dụng slice trong trường hợp này sẽ giúp vận chuyển từng đợt khoảng 100 items và khi xử lý tiếp theo thì lấy tiếp.
Xem ví dụ sau để hiểu hơn https://jsfiddle.net/anonystick/ny1vt0g2/8/
Rất đơn giản nhưng nếu bạn không biết thì sẽ là vấn đề lớn cho việc gửi mail với số lượng lớn. 

Ngoài ra nếu bạn thực sự có kinh nghiệm trong khi sử dụng Promise nhằm nâng cao performance promise làm việc trên code của bạn, thì bạn hãy chú ý đến việc xử lý tuần tự hoặc song song promise. Điều đó giúp bạn rất nhiều khi xử lý dữ liệu lớn.

#Get API

Một trường hợp khác, nếu bạn có task là tổng hợp một thông tin lấy nhiều nguồn từ nhiều API. Không cách nào dễ dàng và tốt khi sử dụng Promise.all trong trường hợp này. 

Ví dụ như thế này:

// Function to fetch Github info of a user.
const fetchGithubInfo = async (url) => {
  console.log(`Fetching ${url}`)
  const githubInfo = await axios(url) 
// API call to get user info from Github.
  return {
    name: githubInfo.data.name,
    bio: githubInfo.data.bio,
    repos: githubInfo.data.public_repos
  }
}

// Iterates all users and returns their Github info.
const fetchUserInfo = async (names) => {
  const requests = names.map((name) => {
    const url = `https://api.github.com/users/${name}`
    return fetchGithubInfo(url) 
// Async function that fetches the user info.
     .then((a) => {
      return a // Returns the user info.
      })
  })
  return Promise.all(requests) 
//Waiting for all the requests to get resolved.
}


fetchUserInfo(['sindresorhus', 'yyx990803', 'gaearon'])
 .then(a => console.log(JSON.stringify(a)))

/*
Output:
[{
  "name": "Sindre Sorhus",
  "bio": "Full-Time Open-Sourcerer ·· Maker ··",
  "repos": 996
}, {
  "name": "Evan You",
  "bio": "Creator of @vuejs, previously @meteor & @google",
  "repos": 151
}, {
  "name": "Dan Abramov",
  "bio": "Working on @reactjs. Co-author of .",
  "repos": 232
}]
*/

Kết luận

Trước khi đi đến kết thúc bài viết hôm nay, các bạn nên tích cự sử dụng và phân biệt khi nào sử dụng Promise, và khi nào sử dụng async/await trong es6. Chúng tôi đã tổng hợp tất cả những bài viết về promise javascript và cách sử dụng chúng, tất cả có trong blog lập trình javascript anonystick.com.

Promise.all là cách tốt nhất để controls một group promise với một promise duy nhất. Đây là một trong những cách mà developers có thể tận dụng được các công việc nhanh hơn. Hy vọng qua bài này, giúp các bạn hiểu thêm và trong trường hợp nào có thể sử dụng chúng.

Happy coding!