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

Đầ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. 

Tham gia cùng chúng tôi:

Facebook: Cộng đồng lập trình javascript

Facebook Thảo luận về Javascript, ReactJS, VueJS, AngularJS Việt Nam

Tất cả những gì bạn biết về Promise.all là gì?

Đó là: 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.

 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. 

#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

Tham gia cùng chúng tôi:

Facebook: Cộng đồng lập trình javascript

Facebook Thảo luận về Javascript, ReactJS, VueJS, AngularJS Việt Nam

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! 

Tham khảo thêm: 

https://developer.mozilla.org/vi/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

https://medium.com/quick-code/javascript-promises-in-twenty-minutes-3aac5b65b887

https://morioh.com/p/4eea45e845b7/all-you-need-to-know-about-promise-all