Admin
12/09/2023
Share
Promise đã được đề cập nhiều trong các bài viết liên quan đến Javascript trước đây. Nếu bạn mới học Javascript, bạn có thể tự hỏi: Promise trong Javascript là gì? Nghe có vẻ khái niệm này khá trừu tượng, phải không?
Promise là một trong những khái niệm quan trọng trong ngôn ngữ Javascript, cần phải hiểu rõ. Nếu bạn chưa biết về 7 khái niệm cơ bản đó, hãy đọc bài viết này: 7 khái niệm cơ bản của Javascript.
Promise không chỉ giải quyết vấn đề callback hell mà còn có nhiều ứng dụng hữu ích khác.
Trong bài viết này, tôi sẽ chia sẻ mọi kiến thức cần thiết về Promise trong Javascript để giúp bạn hiểu rõ hơn về nó.
Trọng tâm của bài viết.
Đầu tiên, tôi muốn nhắc lại định nghĩa của promise để bạn hiểu rõ hơn về ý nghĩa của nó. Theo định nghĩa từ nhà phát hành:
Promise là một công cụ được sử dụng để thực hiện tính toán không đồng bộ. Nó đại diện cho một tiến trình hoặc tác vụ chưa được hoàn thành ngay lập tức. Trong tương lai, promise sẽ trả về một giá trị đã được giải quyết (resolve) hoặc không được giải quyết (reject).
Định nghĩa thì có vẻ khó hiểu vậy thôi. Nhưng thực ra, promise có thể được hiểu đúng như tên gọi của nó: Lời hứa. Nghĩa là một lời hứa có thể được thực hiện (resolve) hoặc không được thực hiện (reject).
Hãy nhớ lại một kỷ niệm thời thơ ấu đầy động lực. Một ngày nắng đẹp, cha nói với mình rằng: “Nếu con ngoan, cha sẽ mua cho con một chú robot khổng lồ vào tuần sau”.
Nếu tuân thủ theo kỹ thuật, thì đây chính là một cam kết. Và cam kết này có thể có 3 tình trạng:
#Lập trình không đồng bộ là gì?
Như định nghĩa của hứa hẹn, thì ý tưởng hứa hẹn được tạo ra để giải quyết vấn đề không đồng bộ.
Vậy mã không đồng bộ là gì?
Để hiểu ý tưởng này, tôi sẽ đảo ngược vấn đề: khái niệm của mã đồng bộ là gì!
Lập trình đồng bộ là quá trình thực hiện các câu lệnh theo thứ tự từ trên xuống dưới, mà câu lệnh sau phải đợi câu lệnh trước hoàn thành. Các câu lệnh có thể là các câu lệnh đơn hoặc nhóm câu lệnh.
Như vậy trái ngược với mã đồng bộ là mã không đồng bộ. Đơn giản như vậy thôi!
≫>Đọc thêm về JS: Cách tối ưu hiệu suất mã nguồn Javascript.
#Các trạng thái của promise trong Javascript
Trong ví dụ trên, chỉ có thể nhắc lại rằng promise có thể ở ba trạng thái khác nhau vào một thời điểm: Pending, Fulfilled, Rejected.
Cú pháp sử dụng cam kết chính thức như sau:
var promise = new Promise (function a(resolve, reject) { if(// task complete) { resolve(value); } else { reject(new Error()); } });
#Cách áp dụng Promise trong Javascript
Để dễ hiểu và thực tế, mình sẽ viết code minh họa cho promise sử dụng ví dụ ở đầu bài viết.
let isDadHappy = false; // Định nghĩa một promise let willILetNewToy = new Promise ((resolve, reject) =>{ if(isDadHappy){ resolve(toy); // Fulfilled } else { reject('Bố trượt lô, nên khỏi mua đồ chơi luôn'); } }) // Cách sử dụng promise trên let askDad = ()=>{ willILetNewToy .then(fulfilled =>{ console.log('Mình nhận được một món đồ chơi từ bố'); }) .catch(reject =>{ console.log('Chả có đồ chơi nào cả.'); }) }
Cũng dễ dàng phải không?
#Tại sao lại sử dụng promise? Ưu điểm của promise trong javascript là gì?
Trước khi có khái niệm promise, chúng ta đã biết đến callback. Callback là một hàm sẽ được thực hiện sau khi một hàm khác hoàn thành (được gọi là callback vì điều này).
Ủa! Nếu như vậy, thì promise chỉ là một dạng callback thôi mà, không có sự khác biệt gì nhau cả.
Ban đầu, khi tôi mới bắt đầu khám phá về promise, tôi cũng có suy nghĩ giống như vậy. Vậy thì promise có những ưu điểm gì so với callback?
Sau khi tra cứu nhiều tài liệu, tôi tạm thời thấy promise có một số lợi ích như:
1. Promise hỗ trợ “chuỗi kết nối”
Promise có một đặc điểm đặc biệt là hàm then(). Hàm .Then() trả về một promise. Điều này cho phép bạn gọi nhiều hàm bất đồng bộ liên tiếp bằng cách sử dụng promise.
// Dùng callback xin_mẹ_mua_xe(function(xe) { chở_gái_đi_chơi(xe, function(gái) { chở_gái_vào_hotel(hotel, function(z) { // Làm gì không ai biết }); }); }); // Dùng promise, code gọn nhẹ dễ đọc xin_mẹ_mua_xe .then(chở_gái_đi_chơi) .then(chở_gái_vào_hotel) .then(function() { /*Làm gì không ai biết*/ });
Nhìn đoạn mã trên, bạn có cảm thấy mã sạch hơn so với cách sử dụng callback không?
2. Xử lý lỗi trong Promise
Phương thức .Catch() trong promise cũng tương tự như phương thức .Then(). Tuy nhiên, nó chỉ được sử dụng khi hàm reject() được gọi. Nghĩa là nó chỉ được sử dụng khi có lỗi xảy ra.
Một ví dụ cho việc này là:
aPromise.catch(doThisWhenItRain); // hoàn toàn giống với aPromise.then(null, doThisWhenItRain);
Hai cách viết trên hoàn toàn tương đồng về logic, chỉ khác nhau là cách viết thứ nhất mang ý nghĩa rõ ràng hơn.
Điều này có ý nghĩa là hàm .Catch() cũng có thể bắt lỗi liên tiếp hoặc đồng thời giống như hàm .Then().
Khi có bất kỳ hàm nào trong đoạn code “xin mẹ mua xe” bị lỗi, promise sẽ bị chuyển sang trạng thái reject và hàm catch sẽ được kích hoạt.
// Khi một function bị lỗi, promise bị reject (thất hứa) function chở_gái_vào_hotel() { return Promise((response, reject) => { reject("Xin lỗi hôm nay em đèn đỏ"); }); } xin_mẹ_mua_xe .then(chở_gái_đi_chơi) .then(chở_gái_vào_hotel) .then(function() { /*Làm gì đó, ai biết*/ }) .catch(function(err) { console.log(err); //"Xin lỗi hôm nay em đèn đỏ" console.log("xui vl"); });
Promise không chỉ giúp giải quyết vấn đề callback hell mà còn cho phép bắt được mọi loại lỗi, từ lỗi throw Error đến lỗi cú pháp lập trình.
Điều này rất quan trọng cho mã nguồn của bạn, tránh bị gặp sự cố.
3. Promises.All()
Trong một số trường hợp, bạn muốn thực hiện và nhận kết quả của nhiều promise đồng thời.
Giải pháp đơn giản nhất mà bạn nghĩ tới lúc này là dùng một vòng lặp, duyệt qua tất cả các promise.
const userIds = [1, 2, 3, 4] // api.getUser() là hàm trả về promise const users = [] for (let id of userIds) { api.getUser(id).then(user => ([...users, user])) } console.log(users) // Kết quả in ra là một mảng rỗng: []
Tại sao lại như thế?
Do nguyên nhân mã chạy không đồng bộ, hàm console.Log() đã được thực hiện trước khi hàm getUser() hoàn thành.
Để giải quyết vấn đề này, chúng ta có một phương án toàn diện, đó là sử dụng hàm static Promise.All().
Phương thức Promise.All([promise1, promise2,…]) Nhận vào một mảng các promises. Nó sẽ chỉ resolve khi và chỉ khi tất cả các promise đã hoàn thành.
Viết mã thì đơn giản như này thôi.
const userIds = [1, 2, 3, 4] Promise.all(usersIds.map(api.getUser)) .then(function(arrayOfResults) { const [user1, user2, user3, user4] = arrayOfResults })