Bàn về async/await trong vòng lặp javascript
Lâu nay mình đang phải làm một dự án sdk javascript nên khá đau đầu với cái bất đồng bộ trong vòng lặp của javascript.Để tránh quên nên mình viết lên đây cho đỡ quên.Bài viết này mình chỉ viết về một số ví dụ sử dụng async/await cho dễ hiểu nhé.
1. Chuẩn bị cho ví dụ sắp tới
Giả sử chúng ta có một giỏ trái cây và số lượng của từng trái cây trong giỏ
const fruitBasket = {
apple: 27,
grape: 0,
pear: 14
}
Bây giờ mình muốn lấy số lượng hoa quả trong giỏ mình sẽ viết một hàm như sau const getNumFruit = fruit => {
return fruitBasket[fruit]
}
const numApples = getNumFruit('apple')
console.log(numApples)
Giờ giả sử fruitBasket được lấy từ server chứ không phải mình khai báo biến như trên nữa việc lấy dữ liệu sẽ mất một vài giây, mình sẽ giả lập công việc lấy dữ liệu từ server như sau
const sleep = ms => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getNumFruit = (s, fruit) => {
return sleep(s).then(v => fruitBasket[fruit])
}
getNumFruit(1000, 'apple')
.then(num => console.log(num)) // 27
Hàm sleep tương ứng với việc lag một vài giây, trong hàm này chúng ta trả về một Promise, các bạn chưa biết Promise là gì thì tham khảo tại đây.Ở hàm getNumFruit mình sửa lại chút tức là sau khi chờ 1s thì mới tiếp tục thực hiện lấy số lượng hoa quả.Sau đó mình gọi hàm getNumFruit như trên ví dụ.Hoặc bây giờ mình sẽ dùng await để lấy số lượng các loại quả trong giỏ, tuy nhiên việc lấy dữ liệu bị gián đoạn khi lấy táo mất 1s, nhưng lấy nho mất tận 3s rồi lấy lê mất 2s.Tuy nhiên nó vẫn sẽ thực hiện tuần tự lấy được táo rồi mới lấy nho cuối cùng mới lấy lê.
const control = async () => {
console.log('Start')
const numApples = await getNumFruit(1000, 'apple')
console.log(numApples)
const numGrapes = await getNumFruit(3000, 'grape')
console.log(numGrapes)
const numPears = await getNumFruit(2000, 'pear')
console.log(numPears)
console.log('End')
}
Từ khóa await chỉ được sử dụng trong hàm async thôi nhé, dưới đây là kết quả
Vậy bây giờ chúng ta thử sử dụng await trong vòng lặp xem sao nhá.
2. Await trong vòng lặp
Bây giờ mình không muốn lấy một loại trái cây nữa mà mình muốn lấy danh sách loại trái cây trong giỏ
và danh sách loại trái cây đấy như sau:
const fruitsToGet = ['apple', 'grape', 'pear']
Trong vòng lặp (for) mình cũng sẽ sử dụng hàm getNumFruit để lấy số lượng các loại quả theo thứ tự nhé. const forLoop = async () => {
console.log('Start')
for (let index = 0; index < fruitsToGet.length; index++) {
const fruit = fruitsToGet[index]
const numFruit = await getNumFruit(1000,fruit)
console.log(numFruit)
}
console.log('End')
}
Khi sử dụng await mình mong đợi sẽ lấy được số lượng trái cây theo thứ tự theo danh sách và kết quả như sauồ nó hoạt động tốt kìa , await hoạt động tốt trong các vòng lặp kiểu truyền thống như for, while, for-of .Tuy nhiên với các vòng lặp có callback như forEach, map, filter, reduce thì thế nào nhỉ.Tiếp tục thử nghiệm với các loại loop có callback nhé.
3.Await trong forEach
Bây giờ mình sẽ thử làm tương tự như ví dụ trên nhưng khác là thay vì dùng for thì mình sẽ thử dùng forEach.
const forEachLoop = () => {
console.log('Start')
fruitsToGet.forEach(async fruit => {
const numFruit = await getNumFruit(1000, fruit)
console.log(numFruit)
})
console.log('End')
}
Chắc các bạn hi vọng nó sẽ trả về kết quả như thế này. 'Start'
'27'
'0'
'14'
'End'
Nhưng không, kết quả lại không mong đợi như mình muốn mà kết quả như sauSở dĩ có việc này là do forEach không nhận biết được promise, forEach không hỗ trợ async và await .Vì vậy chúng ta không thể sử dụng await trong forEach.
Nhận xét
Đăng nhận xét