Thủ thuật Promise để code bất đồng bộ Javascript dễ đọc hơn

Bài viết của VnTalking dưới dạng Guest post

Javascript là ngôn ngữ lập trình vô cùng phổ biến, được sử dụng để xây dựng các ứng dụng từ front end tới back end, từ web app tới mobile app. Được đánh giá là ngôn ngữ dễ học nhưng để mã nguồn không chỉ chạy được mà còn dễ bảo trì, dễ mở rộng lại ở một tầm cao khác.

Bài viết này, mình chia sẻ một khía cạnh nhỏ để code của bạn “sạch, đẹp” hơn. Đó là sử dụng Promise để viết các mã bất đồng bộ trong Javascript.

javascript promise

Promise giúp mã nguồn dễ đọc hơn

Giả sử một bài toán như sau, chúng ta cần lấy dữ liệu từ API nào đó rồi hiển thị dữ liệu ra giao diện, ví dụ: https://catfact.ninja/fact

{
  "fact": "In Japan, cats are thought to have the power to turn into super spirits when they die. This may be because according to the Buddhist religion, the body of the cat is the temporary resting place of very spiritual people.i",
  "length": 220
}

Bình thường, nếu bạn sử dụng Jquery để gọi API sẽ dùng callback như sau:

$.getJSON('https://catfact.ninja/fact', function(data) {
  console.log(data.fact)
  $('body').append(data.fact);
});

Đoạn code rất đơn giản, chúng ta sử dụng Jquery để gọi tới API, khi nào API phản hồi và trả về kết quả sẽ được đẩy vào trong callback function(data).

Với dự án nhỏ thì đoạn code không vấn đề gì cả. Nhưng nếu phát sinh yêu cầu phải gọi thêm API ngay trong callback thì sao? Điều này sẽ dẫn tới vấn đề, đó là callback hell.

Để code được được “đẹp” hơn, chúng ta chuyển sang sử dụng promise như sau:

var promise = $.getJSON('https://catfact.ninja/fact');
 
promise.done(function(data) {
  console.log(data.fact)
  $('body').append(data.fact);
});

Về cơ bản thì nó tương tự như dùng callback đúng không? Trường hợp trên là nếu request thành công, còn nếu thất bại thì sao? Tất nhiên là promise có hàm để xử lý rồi.

var promise = $.getJSON('https://catfact.ninja/fact');
 
promise.done(function(data) {
  console.log(data.fact)
  $('body').append(data.fact);
});
 
promise.fail(function() {
  $('body').append('<p>Đã gặp lỗi, đề nghị AE thử lại!</p>');
});

Cá nhân mình ít khi khai báo biến để lưu instance Promise như trên lắm, mình hay viết tắt như sau:

$.getJSON('https://catfact.ninja/fact')
.done(function(data) {
  console.log(data.fact)
  $('body').append(data.fact);
})
.fail(function() {
  $('body').append('<p>Đã gặp lỗi, đề nghị AE thử lại!</p>');
});

Wapper hàm gọi API

Để cho mã nguồn có tính tái sử dụng cao hơn, chúng ta thường sẽ viết riêng một hàm để chuyên xử lý gọi API. Trong hàm gọi API này, chúng ta sẽ trả về một promise object.

var catfact = {
  html : function() {
    return $.getJSON('https://catfact.ninja/fact').then(function(data) {
      return data.fact;
    });
  }
}

Đối tượng catfact lúc này có một thuộc tính html là một promise object. Điều thú vị là khi viết như này, khi cần phải hiển thị ra giao diện chúng ta không quan tâm khi nào thì hàm gọi API xử lý xong, chỉ quan tâm tới việc hiển thị nó như thế nào mà thôi.

catfact.html().done(function(html) {
  $("body").append(html);
});

Hàm then() của promise cho phép chúng ta sửa đổi kết quả của một promise và chuyển nó cho trình xử lý tiếp theo.

Gọi nhiều Promise liên tiếp

Có lẽ tính năng đáng chú ý nhất của Promise đó chính là khả năng kết hợp nhiều promise với nhau. Nếu bạn dùng callback để xử lý việc này, bạn sẽ làm như nào?

Ví dụ nhé:

var firstData = null;
var secondData = null;
 
var responseCallback = function() {
 
  if (!firstData || !secondData)
    return;
 
  // do something
}
 
$.get("http://example.com/first", function(data) {
  firstData = data;
  responseCallback();
});
 
$.get("http://example.com/second", function(data) {
  secondData = data;
  responseCallback();
});

Có vẻ ổn nhưng hơi dài đúng không? Với Promise nó còn “ngon” hơn nhiều

var firstPromise = $.get("http://example.com/first");
var secondPromise = $.get("http://example.com/second");
 
$.when(firstPromise, secondPromise).done(function(firstData, secondData) {
  // do something
});

Ở đây chúng ta đã sử dụng hàm when() của Promise để gọi một handler khi cả hai request hoàn thành.

Tạm kết

Trên đây là một số thủ thuật, cũng như kinh nghiệm để viết code dễ đọc và dễ bảo trì hơn trong các dự án Javascript sắp tới.

Mình hy vọng những kiến thức này sẽ giúp ích cho bạn trong quá trình học lập trình. Hẹn gặp lại ở bài viết nhé!

Chuyên mục: Chuyện lập trình
Tag: Lập trình web