Ajax

Chuyển đổi JS callback thành Promise

Như tôi đã giới thiệu trong bài Promise, Promise là một công cụ rất hay để giúp bảo vệ mắt tránh khỏi tình trạng lên độ do callback hell gây nên. Thực chất, Promise không ngắn hơn callback, mà ngược lại, còn có thể làm tổng số dòng code dài thêm. Nhưng cái hay của nó không phải nằm ở việc dựng hàm, mà nằm ở phần dùng hàm, đặc biệt là khi bạn tận dụng được các hàm có sẵn trong bộ SDK được cung cấp như Firebase hay Vue. Bài viết này sẽ giúp bạn dễ dàng chuyển đổi từ các callback loằng ngoằng thành chuỗi Promise.

promise

Bây giờ, tôi xin phép được nhắc lại “mớ hổ lốn” callback hell mà tôi đã giới thiệu trong bài Promise. Như bạn có thể thấy, chỉ có bấy nhiêu dòng cỏn con thôi mà đã gây “xốn con mắt bên phải, đỏ con mắt bên trái, mờ cả hai tròng kính”:

Để hạn chế sử dụng Pred forte 5ml (xin mời gugo) và Viroto thì bạn có thể tách hàm ra như bên dưới. Dù vẫn còn thấy khó khăn nhưng mắt bạn sẽ không bị tăng nhãn áp một cách ào ào như bên trên:

Trước khi chuyển đổi thì tôi nhắc lại một chút lí thuyết về Promise. Promise là một object có constructor hẳn hoi với constructor này chứa một function gồm hai tham số. Tên tham số đầu tiên thực chất là một function sẽ chứa một tham số duy nhất và bạn sẽ cho nó thực thi khi bạn muốn Promise thành công, tôi gọi nó là onSuccess, và tham số của nó chính là kết quả trong function trong then; còn teenn tham số thứ hai cũng là một funtion chứa một tham số duy nhất và bạn sẽ thực thi nó khi bạn muốn Promise thất bại, tôi gọi nó là onError, tham số của nó là lỗi trong function trong catch. Để dễ hiểu hơn thì bạn nhìn mấy dòng code đơn sơ này:

Và bây giờ là lúc thực hiện chuyển đổi. Tôi sẽ tiến hành với tên grandParentCallback trước tiên, và ở đây là trường hợp đơn giản nhất, chỉ có thành công mà không có thành thụ. Do đó onError sẽ không được thực thi, và bạn cũng không cần .catch.

Ở đây, các grandParentFunction, parentFunction, childFunction, grandChildFunction là những functions tương ứng với grandParentCallback, parentCallback, childCallback, grandChildCallback nhưng không chứa tham số thứ hai là một callback khác vốn chứa kết quả, mà kết quả (result) đó sẽ được trả về trực tiếp để truyền vào onSuccess(result). Chẳng hạn, nếu bạn có một callback function getEmail(userId, function(email) { … }) thì ở đây bạn sẽ cho getEmail(userId) return chính cái email đó và bỏ nó trong onSuccess(email).

Đối với callbacks thì bạn có thể tách các functions ra cho dễ nhìn, và với Promise bạn cũng có thể làm tương tự luôn và chúng sẽ nhìn giống như cái hình chữ trắng nền tím bên trên. Và tôi cũng “giúp các bạn một vé” với ví dụ luôn. Lưu ý, bạn vẫn phải chuyển đổi các functions như bên trên:

Và khi đính các Promise trên thành chuỗi “lời hứa” thì ta có một chuỗi ngắn gọn như sau:

Ưu điểm của chuỗi Promise là bạn dễ dàng hình dung được các bước bởi các .then ngang hàng nhau, không phải “lồi ra thụt vào” như các callbacks móc nối nhau thành chuỗi dài ngoằng. Nên thực tế, tổng số dòng viết nên một chuỗi Promise thực sự là dài hơn so với callbacks, nhưng việc kết nối các bước triển khai thì vô cùng dễ nhìn. Do đó, nếu bạn có ý muốn “truyền bá” việc sử dụng Promise cho người khác, vui lòng đừng nói là “Promise ngắn hơn so với callback đó mấy chế’, lỡ gặp phải mấy chế “ăn cùng Javascript, ngủ cùng Javascript, đi vệ sinh cũng Javascript” họ sẽ la lối cho, mà thay vào đó, bạn hãy nói “Khi dùng chuỗi Promise thì mọi thứ sẽ dễ nhìn hơn callback nhiều lắm đó mấy chế ợ”.

Trước khi chia tay thì tôi mời bạn nhìn lại phần code trước cái chuỗi bên trên và nhận thấy là chúng ta không còn thấy tên nào ứng với grandParentCallback tương ứng với grandParentPromise? Vì nó nằm luôn trong doSomethingAsync rồi. Giả sử bạn gọi một function mà tạo hóa của nó là một async function, không có tài nào biến nó thành sync function như kiểu getEmail mà tôi nói như bên trên, thì bạn sẽ gọi onSuccess trong callback “của người ta” cho sẵn. Promise sẽ “đợi” cho function chạy xong, onSuccess xong thì mới chạy tới .then liền kề với nó. Điều này là hoàn toàn bình thường và bạn không việc gì phải cuống lên, bế tắc mà cho là “nó là callback rồi thì sao biến thành Promise được”.

Một vấn đề khác là onSucessonError chỉ chứa đúng mỗi một param duy nhất. Vì vậy bạn cố tình gọi onSuccess(resultA, resultB) và onError(error1, error2) thì coi chừng mấy tên sau sẽ bị bỏ qua hoặc nó thảy một error ra console và không thèm chạy tiếp. Vì vậy, bạn sẽ phải… vận dụng kiến thức và kinh nghiệm Javascript của mình ra để xoay sở. Và để tìm ra cách thì bây giờ bạn có thể viết vài callbacks lồng nhau rồi chuyển sang Promise để mài dũa tay nghề cho sắc bén nào.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.