Xử lý Bất đồng bộ bằng Callback, Promise, Async Await hay Observable?

2423

Đặt vấn đề

Javascript là một ngôn ngữ đơn luồng (single thread– chỉ thực hiện một việc tại một thời điểm) và đồng bộ (synchronous).
Nhưng quả thật khi phát triển một trang web ta luôn phải đối mặt xử lý những tác vụ bất đồng bộ  async ( xử lý event như click, mouse, scroll, …hay AJAX call để lấy dữ liệu từ backend) và đôi khi chúng ta cần đa luồng (dữ liệu lớn, ví dụ 10000 items trong một mảng gây ra hiện tượng crash trình duyệt, …).

Như vậy, một ngôn ngữ sync thì xử lí tác vụ async như thế nào?

Thông thường, để xử lý chúng, ta thường dùng các phương pháp như  callbackpromiseasync/await và observable.

Xử lý bất đồng bộ là một trong những nhiệm vụ tất yếu và không thể tránh khỏi. Vì vậy, chúng ta nên tìm hiểu kỹ về nó.

Cùng bắt đầu nhé!

Callback(ES5)

Sử dụng một function làm tham số truyền vào cho một function khác. Sau đó một khoảng thời gian, function được truyền vào sẽ thực thi.

Trong ví dụ trên ta có 3 callback cho document readybutton click eventgetJSON

Nhược điểm của callback là 3 KHÓ: khó maintain, khó đọc, khó debug. Hơn nữa, trường hợp gọi lại quá nhiều callback như vậy dễ dẫn đến tình trạng Callback hell 😭😭

Promise(ES6/ES2015)

Promise như là một “lời hứa” cho một dữ liệu, hành động được hoàn thành(hoặc bị lỗi) của một tác vụ bất đồng bộ và trả về kết quả của nó.

Bất đồng bộ có nghĩa là sẽ hoàn thành sau, chứ không phải ngay lập tức và nó sẽ báo cho ta biết khi nó hoàn thành (hoặc bị lỗi).

Ví dụ: Vào một buổi sáng đẹp trời, bạn đang ngồi uống cafe và tự dưng đói bụng và muốn ăn gì đó. Bạn lên website order một ổ bánh mì tại một cửa hàng nào đó. Cửa hàng thông báo ~10 phút nữa sẽ giao bánh đến cho bạn.
Như vậy, hành động  làm một ổ bánh mì là một tác vụ bất đồng bộ, vì nó cần thời gian là khoảng 10 phút để hoàn thành. Cửa hàng đã cho bạn một Promise là khoảng 10 phút sau sẽ có bánh mì cho bạn. Sau đó, bạn vẫn tiếp tục nhiệm vụ của bạn là uống cà phê và chờ khi nào bành mì giao đến thì bạn sẽ nhận và thưởng thức.

Bạn có thể thấy một Promise có 3 trạng thái sau: chờ-bạn chờ bánh mì giao đến (pending), hoàn thành-giao cho bạn xong (fulfilled), từ chối-không thể làm bánh mì cho bạn vì hết bánh mì (rejected).

Và bài toán callback hell ở mục phía trên được giải quyết như sau:

Một số điểm đặc trưng của promise là:

  • Chỉ trả về một giá trị duy nhất, đó có thể là object, array, number,…
  • Không thể cancel được request.
  • Được khởi tạo ngay lập tức mặc dù chưa có bất kỳ đăng ký nào. Nó không quan tâm bạn đã đăng ký promise hay chưa, miễn là bạn khai báo một promise thì nó sẽ chạy constructor.

Tuy nhiên sử dụng Promise trong một vài trường hợp lại làm phát sinh vấn đề “khá” tương tự Callback là Promise hell.

Async/await(ES7/ES2016)

Để sử dụng hàm async, ta cần khai báo từ khóa async ngay trước từ khóa định nghĩa hàm.

Nghĩa là với hàm định nghĩa với từ khóa function ta phải khai báo ngay trước function, với hàm mũi tên (arrow function) ta phải khai báo trước tập tham số đầu vào, với phương thức của lớp Class thì ta phải khai báo ngay trước tên hàm.

Ta chỉ khai báo một await chỉ trong một async function

Kết quả trả ra của hàm async luôn là một Promise dù bạn có gọi await – có xử lý bất đồng bộ hay không. Promise này sẽ ở trạng thái thành công với kết quả được trả ra với từ khóa return của hàm async, hoặc trạng thái thất bại với kết quả được đẩy qua từ khóa throw trong hàm async.

Ta có thể xử lý ngoại lệ với catch khá đơn giản với từ khóa try - catch hệt như các thao tác đồng bộ:

Trông cứ như các lệnh đồng bộ vậy đúng không nào.

Cho tới nay, các nền tảng và trình duyệt sau đã hỗ trợ hàm async là Node.js v7.6, Chrome v5.55, Microsoft Edge v21.10547.
Trường hợp bạn muốn chạy ở các nền tảng/ trình duyệt chưa hỗ trợ thì có thể dùng  babel để chuyển đổi nhé!

Observable

Observable cũng có những tính năng của Promise và thêm một số ưu điểm khác.
Nó như một ống dữ liệu ( data stream), chúng ta có thể đẩy nhiều dữ liệu qua ống này.

Observable là một khái niệm từ Reactive ProgrammingReactive là một nền tảng xử lý những tác vụ bất đồng bộ thông qua những ống dữ liệu(data stream). Reactive hỗ trợ nhiều ngôn ngữ Java, .NET, … Trong đó có thư viện RxJS hỗ trợ data stream cho các asynctrong Javascript.

Một số điểm đặc trưng của Observable là:

  • Trả về một hoặc nhiều giá trị
  • Có thể cancel request
  • Chỉ được khởi tạo khỉ và chỉ khi có đăng ký đến observable đó(có listener)
  • Có thể retry
  • Có thể dùng với event
  • Có các thao tác tiền xử lý dữ liệu như: filtermap, …

Kết

Vậy là chúng ta đã điểm qua một vài phương pháp xử lý bất đồng bộ trong Javascriptvới CallbackPromiseAsync Await và Observable.
Mong rằng bài viết này sẽ mang lại cái nhìn tổng quan nhất cho các bạn. Chúng ta nên tận dụng sự tiện lợi cũng như hạn chế của từng các hàm  async để chọn ra phương pháp phù hợp để áp dụng cho dự án của mình này nhằm giảm thiểu việc bảo trì trang web sau này.
Happy Coding !!!

Reference: KipalogThauNguyenMedium

Topdev via viblo

Callback function và mutex lock khác gì với channel ?”]