Get in touch
or send us a question?
CONTACT

Promise là khỉ gì ?

Bài này biên về Promise, khá phức tạp, nếu chưa hiểu các bạn có thể in ra đem vào để trong toilet, mổi lần vào đó :poop: thì cầm lên đọc giết thời gian. Mình đã làm và thành công, nhất là những hôm táo bón. Có khi đọc nhập tâm quá :poop: chui ngược vào. Không biết có phải là hiện tượng phản phác quy chân ko nữa. ahihi.

alt text

Ghi chú:

Nếu tên function là dạng tiếng mẻo, thì mình sẽ viết camelCase, vd: doSomething()
Nếu tên function là tiếng việt ko dấu, để dễ đọc mình sẽ viết underscore, vd: dung_khoc_ma_vui_cuoi_len()
Các bạn code kiểu gì thì code, quan trọng là nhất quán, dùng 1 style thôi, đừng như mình !

Bắt đầu chém gió thôi:

Tờ giấy ghi nợ là Promise.

Ai cũng từng 1 lần cho bạn mượn tiền, có đứa hứa sẽ trả (pending), có đứa trả (fulfilled), và cũng có đứa một đi không trở lại (reject).

Đây cũng 3 trạng thái của Promise.

  1. Lúc còn là tờ giấy nợ, pending.
  2. Lúc dc trả tiền, Promise dc resolve, đây là trạng thái fulfilled trong truyền thuyết. Chúng ta cũng có kết quả trả về là số tiền cầm trên tay, ôi những thứ tưởng đã mất khi quay trở về mới thật vi diệu làm sao, bố mày thề ko bao giờ cho mượn tiền nữa.
  3. Lúc bị quịt, Promise bị reject, đây là trạng thái cuối cùng trước khi chúng ta lao ra quán nhậu để chia tay 1 cuộc tình. Có thể là tình yêu, hoặc là tình bạn.

Khi nào dùng Promise.

Khi 1 function đang kẹt nhưng hứa sẽ trả tiền cho chúng ta vào 1 tương lai xa. Nhưng chúng ta lại cần dùng số tiền đó trong 1 function khác. Khi đấy Promise sẽ đóng vai trò là giá trị chúng ta cần, để chúng ta có thể gán nợ ngay, mà ko cần đợi thằng kia trả tiền.

Lúc này anh em cứ nghĩ Promise là 1 tờ giấy lộn, nhưng có tác dụng thật, là minh chứng (proxy) cho cục tiền nợ có nguy cơ mất 99% kia.

tra_tien_em_anh_oi() // trả về 1 Promise.
  .then(function(tien) {
    // chúng ta có thể làm gì đó với số tiền này ngay lập tức
    // ơ làm gì đây
    return nhau_an_mung(tien);
  })
  .catch(function(ly_do_quit){
    console.log(ly_do_quit);
  })

tra_tien_em_anh_oi() sẽ ko phun ra xu nào, mà chỉ trả về tờ giấy nợ Promise. Tạm thời chưa nói đến cách tạo. Anh em chú ý thằng .then và .catch đây là 2 method của Promise.prototype, chỉ gọi dc khi new Promise lên.

Nếu chưa biết prototype là gì thì anh em tham khảo bài biên này http://kipalog.com/posts/prototype-la-khi-gi-

.then

Con hàng này dc gọi khi Promise resolve, tất nhiên khi đấy chúng ta sẽ lấy dc tiền, và làm gì với số tiền đó là tuỳ chúng ta.

Muốn .then nhiều lần cũng dc, khi đấy Promise sẽ thực hiện các function trong .then theo thứ tự. Tụi mẻo gọi vụ này là Chaining. Đây là lý do nhiều ng thích Promise, vì nó giúp chúng ta tổ chức và code rất kotex.


tra_tien_em_anh_oi() 
  .then(function(tien) {
    return nhau_an_mung(tien);
  })
  .then(function(tien_con_lai_sau_khi_nhau){
    return mat_xa(tien_con_lai_sau_khi_nhau);
  })
  .then(function(){ // het tien 
    return hue_oi();
  });

Lưu ý trong cái .then thứ 2, giá trịtien_con_lai_sau_khi_nhau là kết quả của function nhau_an_mung, sau đó tiền, à ko có sau đó nữa vì đã hết tiền, .then cuối cùng ko có giá trị gì truyền vào cả !

Anh em cũng chú ý là .then sẽ nhận vào 1 function, nếu chọi cái khác vào, tuy nó cho phép chúng ta có quyền im lặng .then(null) nhưng những gì chúng ta nói, nó sẽ coi như chưa nói gì cả. Ngay cả truyền 1 Promise cho nó, nó cũng lơ luôn. Ăn có thể ăn bậy, nhưng nói không thể nói bậy. Luôn truyền cho .then một function nhoé.

Và nhớ return, return 1 Promise khác cũng dc, mà 1 giá trị tuỳ ý cũng dc. Cái này anh em cứ buông tay tuỳ tiện mà làm. Ngay cả return null cũng viết vào luôn, cho nó rõ ràng.

.catch

Trong trường hợp tra_tien_em_anh_oi ko trả tiền, tức là Promise bị reject, thì con hàng này sẽ chạy, tất nhiên là ko có tiền, nhưng chúng ta sẽ nhận dc lý do quịt nợ một cách lịch sự, có thể chỉ là 1 string, hoặc cũng có thể là 1 exception. Nói chung chúng ta sẽ có lý do, để còn báo cáo lại cho quan bà ở nhà.

Promise ưu việt hơn callback ở chổ chúng ta có thể khạc nhổ exception một cách bừa bãi mà ko lo lắng vì .catch sẽ lụm dc hết.**

Viết function tra_tien_em_anh_oi

function tra_tien_em_anh_oi() {
  // tạo và trả về 1 Promise, khi này Promise ở trang thái pending
  return new Promise(function(resolve, reject) {
    // tâm sinh lý ngẫu nhiên 
    var isHappy = Math.random() >= 0.5;

    // nếu vui thì gọi resolve để trả tiền 
    if (isHappy) {
      var tien = 1000;
      return resolve(tien); //  Promise dc fulfilled  
    }

    // không vui quịt luôn
  // nhớ cho ng ta biết lý do vì chúng ta là lập trình viên lịch sự.
    var reason = 'lịt pẹ bố dek trả đấy làm gì nhau';
    reject(reason); //  Promise ở trạng thái reject
  });
}

Promise Hell

Tuy nói Promise giúp tổ chức code, và tránh callback hell, nhưng nếu viết code ko khô thoáng và sạch sẽ thì anh em vẫn rơi vào Promise Hell. Do đó lúc nào chúng ta cũng phải dùng kotexcode.

Promise Hell vì nested .then ngu.

  tra_tien_em_anh_oi()
    .then(function(tien){
      return nhau_an_mung(tien)
        .then(function(tien_con_lai){
          return mat_xa(tien_con_lai)
            .then(function(){
              return hue_oi();
            })
        })
    })

Viết lại thành Kotexcode

  tra_tien_em_anh_oi()
    .then(nhau_an_mung)
    .then(mat_xa)
    .catch(console.error.bind(console));

Các vấn đề thường gặp

Sau đây là kinh nguyệt và các tâm đắc khi dùng Promise, anh em có thể tham khảo và góp ý hộ iem.

Code đang sync, ngứa nên covert qua async

// code sync
function nhau_an_mung() {
  return 500;
}

// wrap lại bằng Promise
function nhau_an_mung() {
  return Promise.resolve(500);
}

Tỉ dụ ta có functionve_nha, chỉ chạy khi đã uong_ruou và hut_thuoc, mà 2 sở thích đời thường này lại có thể cùng lúc tiến hành. Cho nhanh chết.


// thay vì
e_nhau_khong()
  .then(uong_ruou)
  .then(hut_thuoc)
  .then(ve_nha)
  .catch(console.log);

// nên viết thế này
e_nhau_khong()
  .then(function(){
    return Promise.all([
      uong_ruou(),
      hut_thuoc()
    ])    
  })
  .then(ve_nha)
  .catch(console.error.bind(console));

Cách viết đầu tiên chậm vì muốn đốt điếu thuốc phải ráng nhịn đến khi tàn cuộc nhậu, tỉ dụ uong_ruou cần 1 tiếng, hut_thuoc cũng 1 tiếng thì ta cần 2 tiếng mới về đến nhà.

Cách viết thứ 2 dùng Promise.all code chạy nhanh gấp nhiều lần so với cách 1 vì uong_ruou và hut_thuoc sẽ dc chạy song song, cùng lúc ( parallel ) nên chỉ tốn 1 tiếng là đã xong. Nhanh gấp đôi.

Kinh nguyệt là cái gì chạy parallel dc thì cho chạy parallel hết. Tốc độ cải thiện rất vi diệu !

Một ví dụ khác, ta cần đi thư viện mượn sách, để đi ta cần tiền và thẻ sinh viên, mà thẻ sinh viên thì phải có tiền vì hôm qua thua bóng đem cầm mẹ rồi. (case này giả tỉ chuộc thẻ ko mất tiền nhoé)

Anh em nghe mùi chắc cũng biết nested .then

function findMoney()
  .then(function(money){
    // findMoney trả về .. money
    // truyền money vào findCard để chuộc thẻ sinh viên
    return findCard(money)
      .then(function(card){
        // có thẻ thì đi thư viện tìm sách
        return findBooks(money, card) // findBooks cần tiền và sách từ các Promise trên
          .then(function(books){
            // lựa sách 18+ đọc thôi
            return readEpicBooks(books.filter(function(book){
              return !! (book.categoryName === '18+');
            }));
          });
      });
  })
  .catch(console.error.bind(console));

Trường hợp như trên nested .then hơi khó tránh tuy nhiên muốn tránh vẫn có nhiều cách, ví dụ như dùng scope chẳng hạn.

Anh em nào chưa biết scope thì xem ở đây http://kipalog.com/posts/scope-va-closure-la-khi-gi-

var money
findMoney()
  .then(function(_money){
    money = _money;
    return findCard(money);
  })
  .then(function(card){
    return findBook(money, card);
  })
  // .....

Hoặc cũng có thể dùng Promise.all

var money = findMoney();
var card = moneyPromise.then(findCard);

Promise.all([money, card])
  .then(function(res){
    return findBooks(res[0], res[1]);
  })
  .then(console.log.bind(console))
  .catch(console.error.bind(console))

Có điều nếu chỉ nested .then 1 cấp thì cũng ko cần phải tự tay bóp dái chi cho đau. Kinh nguyệt là tuỳ trường hợp nếu thấy rối và khó đọc thì chúng ta mới cần refactore.

Túm cái váy lại

Promise.resolve('em xin hết ạ');

à mà quên, câu hỏi có trúng chưởng:
Code sau đây ngu ở chổ nào, hãy đưa ra 10 lý do chứng minh quocnguyen đẹp trai. :joy: :joy: :joy:

doSomething().then(function () {
  doSomethingElse();
});

source: https://kipalog.com/posts/Promise-la-khi-gi-