Tại sao mã của bạn không hoạt động: Sự thật đằng sau việc sử dụng “async/await” với “forEach” trong JavaScript

Tram Ho

Giới thiệu

Bạn đã bao giờ được thông báo rằng bạn không thể sử dụng “async/await” với “forEach” trong JavaScript chưa? Một trong những thành viên trong nhóm của chúng tôi gần đây đã được thông báo về điều này và điều đó khiến chúng tôi tự hỏi – tại sao lại như vậy? Trong bài viết này, chúng ta sẽ xem xét kỹ hơn chủ đề này và giải thích tại sao đây là một quan niệm sai lầm phổ biến.

Vấn đề

Chúng ta hãy xem đoạn mã sau:

Trong mã này, chúng tôi có một hàm không đồng bộ “testAsync” nhận một giá trị và trả về giá trị đó cộng với 1 sau độ trễ 100 mili giây. Sau đó, chúng tôi có một mảng “dữ liệu” trống và một dãy số “params”. Chúng tôi sử dụng phương thức “forEach” để lặp qua “params” và gọi “testAsync” cho từng giá trị. Sau đó, chúng tôi ghi lại kết quả và đẩy nó vào mảng “dữ liệu”. Kỳ vọng là mảng “dữ liệu” sẽ kết thúc bằng [1, 2, 3], nhưng trên thực tế, nó là một mảng trống.

Đây là nơi mà sự nhầm lẫn bắt đầu. Ý tưởng là bằng cách sử dụng “await” bên trong hàm gọi lại “forEach”, chúng ta đang đợi chức năng không đồng bộ hoàn thành trước khi chuyển sang bước lặp tiếp theo. Nhưng đây không phải là trường hợp.

quan niệm sai lầm

Vấn đề ở đây là người viết đoạn mã này đang hiểu sai cách hoạt động của “await”. “Đang chờ” chỉ hoạt động khi nó được sử dụng bên trong chức năng “không đồng bộ”. Trong ví dụ trên, chúng tôi đang sử dụng “async” trên chức năng gọi lại được chuyển đến “forEach”, nhưng điều này không giống với việc sử dụng “async” trên chức năng bên ngoài. Chức năng gọi lại được chuyển đến “forEach” không phải là chức năng “không đồng bộ” và do đó, “chờ đợi” không hoạt động như dự định.

Đây là nơi xuất hiện quan niệm sai lầm. Nhiều người nghĩ rằng bằng cách sử dụng “async” trên hàm gọi lại được chuyển cho “forEach”, họ đang làm cho hàm đó không đồng bộ. Nhưng đây không phải là trường hợp. Chức năng gọi lại vẫn là một chức năng đồng bộ và do đó, “chờ đợi” không hoạt động bên trong nó.

Giải pháp

Vì vậy, chúng ta có thể làm gì để khắc phục vấn đề này? Một giải pháp là sử dụng vòng lặp “for” thông thường thay vì “forEach”. Trong trường hợp này, “chờ đợi” sẽ hoạt động như dự kiến.

Mã này sẽ cho chúng ta kết quả mong đợi là [1, 2, 3].

Một giải pháp khác là sử dụng phương thức “map” và sau đó là “Promise.all” để đợi tất cả các lời hứa hoàn thành.

Điều này sẽ cho chúng ta kết quả mong đợi tương tự của [1, 2, 3].

Phần kết luận

Tóm lại, sử dụng “async/await” với “forEach” trong JavaScript là một quan niệm sai lầm phổ biến. Vấn đề là mọi người đang hiểu sai cách hoạt động của “await” và nghĩ rằng bằng cách sử dụng “async” trên hàm gọi lại được chuyển cho “forEach”, họ đang làm cho hàm đó không đồng bộ. Tuy nhiên, chức năng gọi lại vẫn đồng bộ và “chờ đợi” không hoạt động bên trong nó. Để khắc phục sự cố này, chúng ta có thể sử dụng vòng lặp “for” thông thường hoặc phương thức “map” với “Promise.all” để đợi tất cả các lời hứa hoàn thành. Tôi hy vọng bài viết này đã giúp giải tỏa mọi nhầm lẫn và giờ bạn đã hiểu rõ hơn về cách hoạt động của “async/await” và “forEach” trong JavaScript.

Thưởng

Ngoài ra, đây là một ví dụ về cách trực quan hơn để sử dụng “async/await” với “forEach” trong JavaScript.

Trong ví dụ này, chúng tôi đang thêm chức năng “forEachAsync” vào nguyên mẫu Array. Hàm này hoạt động giống như hàm “forEach” thông thường nhưng nó trả về một lời hứa sẽ giải quyết khi tất cả các cuộc gọi lại được chuyển đến nó đã được giải quyết. Điều này có nghĩa là chúng ta có thể sử dụng “await” để đợi tất cả các cuộc gọi lại hoàn tất trước khi tiếp tục. Kết quả là, trong đoạn mã cuối cùng, chúng tôi sẽ nhận được đầu ra “1 2 3 [1, 2, 3]” là kết quả mong đợi.

Điều đáng chú ý là việc triển khai forEachAsync này cũng có thể gặp sự cố tương tự như các ví dụ mã trước đó nếu lệnh gọi lại được chuyển tới nó không đồng bộ hoặc nếu lệnh gọi lại sử dụng await bên trong một hàm không đồng bộ.

Cách tiếp cận này cũng không được coi là một phương pháp hay, vì việc sửa đổi nguyên mẫu Array có thể dẫn đến kết quả không mong muốn và xung đột đặt tên với mã khác. Tốt hơn là sử dụng một giải pháp rõ ràng hơn, giống như những giải pháp đã đề cập trước đó.

Và cuối cùng

Như mọi khi, tôi hy vọng bạn thích bài viết này và học được điều gì đó mới. Xin cảm ơn và hẹn gặp lại các bạn trong những bài viết tiếp theo!

Nếu các bạn thích bài viết này thì hãy cho mình 1 like và subscribe để ủng hộ mình nhé. Cảm ơn bạn.

Giới thiệu

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo