12/08/2018, 15:51

Giới thiệu về Promise trong JavaScript (ES6)

Xin chào mọi người, Chắc hẳn trong mỗi người lập trình viên chúng ta, Khi lập trình với ngôn ngữ Javascript đều thấy được một vấn đề là Bất đồng bộ. Để giải quyết vấn đề này, cách cơ bản nhất là chúng ta sử dụng Callback. Nhưng có một vấn đề xảy ra, đó là việc sử dụng qua nhiều callback dẫn dến ...

Xin chào mọi người, Chắc hẳn trong mỗi người lập trình viên chúng ta, Khi lập trình với ngôn ngữ Javascript đều thấy được một vấn đề là Bất đồng bộ. Để giải quyết vấn đề này, cách cơ bản nhất là chúng ta sử dụng Callback. Nhưng có một vấn đề xảy ra, đó là việc sử dụng qua nhiều callback dẫn dến tình trạng code rất rối, khó maintain. Người ta còn gọi vấn đề này là CallbackHell. Ví dụ:

getData(function(a){  
    getMoreData(a, function(b){
        getMoreData(b, function(c){ 
            getMoreData(c, function(d){ 
                getMoreData(d, function(e){ 
                    ...
                });
            });
        });
    });
});

Nếu cứ dùng callback lồng nhau thế này thì thực sự không ổn. Để giải quyết vấn đề trên Promise đã được ra đời, Nó giải quyết vấn đề CallbackHell hiệu quả bằng cách tạo các Promise Chain, Mình sẽ giới thiệu về Promise Chain sau. Từ phiên bản JavaScript ES6 Promise đã được giới thiệu đến lập trình viên và đã được cho vào thư viện chuẩn của Javascript và các base Promise API hầu hết đã có trên các trình duyệt hiện đại.

Khởi tạo một promise

var myFirstPromise = new Promise(function(resolve, reject){
  // do something asynchronous which eventually calls either: (1)
  //
  //   resolve(someValue); // fulfilled (2)
  // or
  //   reject("failure reason"); // rejected (3)
});

Khởi tạo một đối tượng Promise với tham số là một function với 2 tham số là 2 callback resolve và reject.

Dòng 1: pending (Đang xử lý) trạng thái thứ nhất của Promise, đoạn này chứa các đoạn code bất đồng bộ, Ví dụ lấy dữ liệu từ phía server và hiển thị kết quả ra màn hình. Dòng 2: fulfilled: (Xử lý hoàn thành) trạng thái thứ hai của Promise, đoạn này chứa một callback resolve() implement các đoạn code đồng bộ bên trong (Nếu xử lý code bất đồng bộ phía trên thành công). Dòng 3: rejected: (Xử lý thất bại) trạng thái thứ ba của Promise, đoạn này chứa đoạn callback reject()implement các đoạn code đồng bộ bên trong (Nếu xử lý code bất đồng bộ phía trên thất bại).

Implement Promise

Giờ hãy đi vào một ví dụ thực tế đầu tiên để hiểu hơn về 3 trạng thái trên:

var myFirstPromise = new Promise(function(resolve, reject){
  var request = new XMLHttpRequest();

  request.open('GET', 'https://www.random.org/integers/?num=1&min=1&max=6&col=1&base=10&format=plain&rnd=new');
  request.onload = function() {
    if (request.status == 200) {
      resolve(request.response); // xử lý thành công khi server response thành công.
    } else {
      reject(Error(request.statusText)); // xử lý thất bại khi server response lỗi.
    }
  };

  request.onerror = function() {
    reject(Error('Error fetching data.')); // xử lý thất bại khi request lỗi.
  };

  request.send(); //gửi request
 });
});

Chúng ta khởi tạo một Promise.

Pending: đoạn code bất đồng bộ chính là đoạn khởi tạo và gửi request lên server. Ở đây chúng ta gửi GET HTTP tới random.org để lấy 1 số random từ 0 -> 10 Resolve: Hàm resolve() Reject: Hàm reject() Chú ý: resolve và reject không phải là 2 hàm cố định, chúng ta có thể đổi tên hàm nếu muốn, Nhưng vì ý nghĩa chúng khá sát nên theo mình không nên đổi. Chúng ta đã tạo đối tượng Promise xong, Nhưng nó chưa được Implement:

promise.then(function(data) {
  console.log('Got data! Promise fulfilled.');
 console.log('Random number is: ' + data);
}, function(error) {
  console.log('Promise rejected.');
  console.log(error.message);
});

Để implement một Promise chúng ta sử dụng then()method, một Prototype của Promise, với 2 tham số là 2 hàmresolve() (in ra data) và reject() (xử lý lỗi) Ngoàithen() Promise còn cung cấp một Prototype khác để xử lý lỗi, Bất kì Exception nào sinh ra đều được xử lý, đó là hàm catch() Vậy chúng ta có thể viết lại dòng Implement Promise trên theo một cách khác, Mình nghĩ nên dùng cách này vì code rõ ràng, dễ hiểu và đặc biệt xử lý được tất cả ngoại lệ, Đương nhiên là chúng ta có thể dùng try() catch()để bắt lỗi nhưng điều đó là không nên vì catch() trong Promise đã có rồi.

promise.then(function(result) {
  console.log('Got data! Promise fulfilled.');
  console.log('Random number is: ' + data);
}).catch(function(error) {
  console.log('Error occurred!', error);
});

Promise chain

Promise chain chính là giải pháp cho CallbackHell mình đã đề cập ở trên, chúng ta đi thẳng vào một ví dụ:

var firstMethod = function () {
  var promise = new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log('first method completed');
      resolve({data: '123'});
    }, 2000);
  });
  return promise;
};


var secondMethod = function (someStuff) {
  var promise = new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(someStuff);
      resolve({newData: someStuff.data + ' some more data'});
      console.log(someStuff);
    }, 2000);
  });
  return promise;
};

var thirdMethod = function (someStuff) {
  var promise = new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log('third method completed');
      resolve({result: someStuff.newData});
      console.log(someStuff);
    }, 3000);
  });
  return promise;
};

firstMethod().then(secondMethod).then(thirdMethod); (implement)

Chúng ta khởi tạo 3 function và cả 3 function đều trả về một promise object. Đầu tiên chúng ta chạy dòng (implement) Sau đây mình xin phân tích các luồng xử lý của đoạn code trên. firstMethod() được chạy: Một promise object được khởi tạo.Promise này khi và chỉ khi được implement bằng method then(). Nó chứa 1 function timeout sau 2000 giây nó sẽ console ra dòng 'first method completed' và thực thi tiếp resolve() function với biến truyền vào là chuỗi JSON {data: '123'}. (1)

firstMethod().then(secondMethod): Bản thân firstMethod() trả về một object chính là promise_1, Điều này tương đương với promise_1.then(secondMethod) với secondMethod chính là resolve()function ở (1), someStuff truyền vào ở đay chính là JSON {data: '123'}ở (1). Tiếp tục secondMethod sẽ trả về một đối tượng promise khác, điều này tương đương với firstMethod().then(secondMethod)sẽ trả về 1 object promise_2

firstMethod().then(secondMethod).then(thirdMethod): điều này tương đương với promise_2.then(thirdMethod) Chúng ta có thể thấy các đoạn code tạo ra 1 chuỗi các promise implement bằng method then(), đương nhiên chúng ta có thể catch() sau mỗi then() để nó xử lý exception: Ảnh: https://viblo.asia/uploads/ae3d43d1-0748-4770-a50b-29a8c4c78e16.png

Tổng kết

Trên đây là những kiến thức nho nhỏ về base của Promise, Một cách tốt hơn để xử lý bất đồng bộ trong javascript, Xin cảm ơn!

Tham khảo

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise https://viblo.asia/p/gioi-thieu-ve-promise-trong-javascript-mrDGMJVPezL

0