Callback hell là gì và cách ngăn chặn năm 2024
JavaScript là đồng bộ. Điều này có nghĩa là nó sẽ thực thi code của bạn theo thứ tự sau khi hoiting. Trước khi code thực thi, các khai báo
9 và
0 được "đẩy" lên phía trên cùng scope (phạm vi) của chúng. Đây là ví dụ về code đồng bộ:
Đoạn code này chắc này chắc chắn sẽ in ra "1 2 3". Các request bất đồng bộ sẽ chờ timer kết thúc (setTimeout) hoặc request được đáp trả, trong khi phần còn lại của code tiếp tục được thực thi. Sau đó, khi đến thời điểm callback sẽ đưa các request bất đồng bộ vào hoạt động. Đây là một ví dụ về code bất đồng bộ:
Đoạn code trên sẽ in ra "1 3 2", trong đó "2" ở trong hàm
1, cái sẽ chỉ thực thi sau 2 giây. Ứng dụng của bạn sẽ không chờ cho đến khi 2 giây kết thúc. Thay vào đó nó sẽ tiếp tục thực thi phần còn lại của code và khi timeout kết thúc nó sẽ quay trở lại để thực thi hàm
2. Bạn có thể hỏi "Tại sao điều này lại hữu ích?" hay "Làm thế nào tôi biến code bất đồng bộ (async) thành đồng bộ (sync)?". Hi vọng rằng tôi sẽ cho bạn thấy câu trả lời. Vấn đềMục tiêu của chúng ta là tìm kiếm một người dùng GitHub và lấy tất cả các repo (kho) của người đó. Và bởi vì chúng ta không biết chính xác tên người dùng. Vì thế chúng ta liệt kê tất cả người dùng có tên tương tự nhau và các repo tương ứng. Như thế này: Trong những ví dụ này, chúng ta sẽ sử dụng XHR (XMLHttpRequest). Bạn có thể thay thế nó với jQuery
3 hoặc một hướng tiếp cập tự nhiên hơn gọi là
4. Tất cả sẽ cho bạn các promise ở đầu ra. Đoạn code dưới đây sẽ thay đổi một chút phụ thuộc và hướng tiếp cận của bạn, nhưng bắt đầu sẽ như thế này:
Hãy nhớ rằng, trong những ví dụ này phần quan trọng không phải là kết quả cuối cùng của code. Thay vào đó, mục tiêu của bạn là phải hiểu được sự khác biệt của các cách tiếp cận và cách bạn có thể tận dụng chúng cho công việc của mình. CallbackBạn có thể lưu một hàm trong một biến khi sử dụng JavaScript. Sau đó, sử dụng chúng như các tham số của một hàm khác để thực thi sau đó. Đây chính là "callback". Một ví dụ sẽ là:
Sử dụng
5 cho phép chúng ta làm một số thứ với hàm
6 đã định nghĩa trước đó:
Hàm request sẽ có thêm một tham số
5, và khi một
6 được tạo nó sẽ được gọi trong cả 2 trường hợp lỗi hoặc thành công.
Phân tích:
Lưu ý: Sử dụng
3 như là tham số đầu tiên của callback là một quy ước khá phổ biến, đặc biệt khi sử dụng Node.js. Một hướng tiếp cần "hoàn chỉnh" và dễ đọc hơn sẽ có một vài hàm xử lý lỗi. Chúng ta cũng tách callback ra khỏi hàm request. Giống như thế này:
Đoạn code này có vấn đề là bạn không thể điều khiển thứ tự nhận danh sách repo của người dùng. Chúng ta đang yêu cầu thông tin cho tất cả người dùng trong trường hợp có nhiều hơn 1. Và không thể quyết định được thứ tự sẽ nhận. Ví dụ, danh sách repo của người dùng thứ 10 có thể nhận được đầu tiên và người dùng thứ 2 thì nhận được cuối cùng. Có một giải pháp khả thi cho vấn đề này sẽ được đề cập ở phần sau. Vấn đề chính với callbacks là việc bảo trì và tính dễ đọc. Đây được gọi là callback hell, cái có thể tránh được với cách tiếp cận ở phần tiếp theo. Hình ảnh lấy từ [đây](https://medium.com/@sagish/node-with-benefits-using-coffeescript-in-your-stack-e9754bf58668) PromiesPromises có thể làm cho code của bạn dễ đọc hơn. Một lập trình viên mới có thể xem code base và thấy rõ thứ tự thực thi của code. Để tạo một promise bạn có thể sử dụng:
Hãy phân tích đoạn code trên:
Những điều cần ghi nhớ:
Lưu ý: Bạn có thể tạo ra các promise mà không cần thực thi ngay tại thời điểm khai báo. Hãy sử dụng ví dụ request với một promise để minh họa
Trong trường hợp này khi bạn thực thi
6 nó sẽ trả về như thế này: Một promise đang chờ resolved hoặc rejected
Đây là cách chúng ta xử lý thứ tự kết quả trả về và một vài vấn đề xử lý lỗi. Code vẫn còn hơi phức tạp. Nhưng đó là một cách để cho bạn thấy rằng cách tiếp cận này cũng có thể tạo ra các vấn đề gây khó đọc. Chúng ta có thể sửa nhanh để tách các callback ra như thế này:
0 Bằng cách nhìn vào chuỗi
0 đằng sau
3 bạn có thể biết được cái chúng ta mong đợi từ đoạn code này. Mọi thứ đều có nhiệm vụ riêng. Đây chỉ là "bề nổi" về Promises. Để có một cái nhìn sâu sắc về cách chúng làm việc tôi khuyên bạn nên đọc thêm bài viết này. GeneratorsMột hướng tiếp cận khác là sử dụng generators. Phần này hơi nâng cao một chút vì thế nếu mới bắt đầu có thể thoải mái bỏ qua phần này. Generators cho phép bạn viết code bất đồng bộ giống như đồng bộ. Chúng được biểu diễn bằng dấu
4 trong một hàm như thế này:
1 Thay vì trả lại với câu lệnh
8, generators có một câu lệnh
6. Nó dừng thực thi hàm cho đến khi một phương thức
7 được gọi. Tương tự với
0 của promise chỉ thực thi khi resoled được trả lại. Hàm request của chúng ta sẽ trông như thế này:
2 Chúng ta muốn
9 như một tham số. Nhưng thay vì thực thi request ngay lập tức, chúng ta chỉ muốn khi có một callback để xử lý kết quả trả về (response).
0 sẽ như thế này:
3 Nó sẽ:
Đoạn code để thực thi sẽ như thế này:
4 Bạn sẽ thấy đoạn code này có những vấn đề tương tự callback hell. Giống như async/await, một trình biên dịch được khuyến khích. Bởi vì nó không được hỗ trợ trong các trình duyệt cũ. Ngoài ra, theo kinh nghiệm của tôi generators không phổ biến. Vì thế có thể phát sinh nhầm lẫm trong code base được bảo dưỡng bởi nhiều lập trình viên khác nhau. Cách generators làm việc có thể được tìm thấy trong bài viết này và đây là một tài nguyên khác. Async/AwaitPhương thức này giống như pha trộn generators với promises. Bạn chỉ cần khai báo function nào là
0. Và phần nào trong code sẽ có
1 để
2 kết thúc.
5 Trong kịch bản này:
Áp dụng vào hàm
6 chúng ta sẽ để nó như một
2 đã thấy trước đó:
6 Chúng ta tạo hàm
0 với các
1 như thế này:
7 Bây giờ chúng ta có một hàm async
3, cái sẽ xử lý các request. Một hàm async là cần thiết trong
4 vì chúng ta có danh sách
5 của mỗi người dùng. Chúng ta gọi hàm
3:
8 Phương pháp này và promises là những phương pháp yêu thích của tôi vì chúng dễ đọc và dễ thay đổi. Bạn có thể đọc nhiều hơn về async/await ở đây. Một nhược điểm của việc sử dụng async/await là chúng không được hỗ trợ trong các trình duyệt cũ ở phía front-end. Ở phía back-end bạn phải sử dụng Node 8 trở lên. Bạn có thể sử dụng một trình biên dịch như babel để giải quyết vấn đề này. Giải phápBạn có thể xem source code sử dụng async/await ở đây. Bạn cũng có thể tự thử những cách khác nhau đã được đề cập trong bài viết này. Kết luậnTùy thuộc vào từng tình huống mà bạn có thể sử dụng:
Nó phụ thuộc vào mục đích của bạn. Và cái nào sẽ dễ hiểu với những người khác và với chính bạn trong tương lai để có thể dễ dàng bảo trì code. Đọc thêmhttps://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html https://codeburst.io/generators-in-javascript-1a7f9f884439 https://ponyfoo.com/articles/understanding-javascript-async-await http://chrisbuttery.com/articles/synchronous-asynchronous-javascript-with-es6-generators/ https://medium.com/codebuddies/getting-to-know-asynchronous-javascript-callbacks-promises-and-async-await-17e0673281ee |