[Javascript] Promise – Lời hứa ngọt ngào (P2 – hết)

        Ở phần trước, mình đã giới thiệu qua Promise là gì và tính chất xử lí bất đồng bộ của nó. Trong phần tiếp theo này, chúng ta sẽ xem xét liệu việc xử lí bất đồng bộ của Promise cung cấp có gì đặc biệt, nó giúp ích ta như thế nào, và tại sao chúng ta nên dùng nó?

        Thế giới trước khi có promise trông như thế nào? Ta sẽ xem sự xuất hiện của promise có thực sự “ngọt ngào” như tên gọi của nó hay không nhé.

Hàm xử lí tuần tự – Hàm xử lí bất đồng bộ

        Trước tiên ta sẽ đưa ra một ví dụ để hình dung rõ hơn hai loại hàm xử lí này, đầu tiên là hàm xử lí tuần tự, nơi mà mọi thao tác được xử lí nối tiếp nhau một trình tự chính xác đã được viết ra:

// add two numbers normally
function add (num1, num2) {
   return num1 + num2;
}

// call the function, you get result = 3 immediately
var result = add(1, 2);

        Nếu bạn cần làm nhiều phép toán cộng, bạn chỉ việc viết chúng nối tiếp nhau, bởi vì hàm sẽ trả về kết quả ngay lập tức nên bạn có thể dùng được kết quả này để tính toán ngay sau đó. Ví dự cộng 4 số liên tiếp:

var num1, num2, num3;

// call the function to calculate 1 + 2 + 3 + 4. Result is: 10
num1 = add(1, 2);
num2 = add(num1, 3);
num3 = add(num2, 4);

print("1 + 2 + 3 + 4 = " + num3);

         Bạn đã quá quen với hàm xử lí tuần tự phải không nào, code bạn viết có thứ tự như thế nào thì kết quả chạy sẽ y như vậy, không có điều gì là quá khó hiểu cả. Bây giờ chúng ta sẽ xem xét tới các hàm xử lí bất đồng bộ, giả sử bạn cần truyền input là 2 số nguyên về một hàm xử lí ở server, server sẽ thực hiện tính toán và đẩy kết quả trả về cho bạn, code sẽ trông như sau:

// add two numbers remotely, by calling an API
var result = getAddResultFromServer('http://www.example.com?num1=1&num2=2');

// print the result, you get result  = "undefined"
console.log(result);

        Bạn có biết lí do tại sao khi in ra kết quả thì lại xuất hiện giá trị “undefined” không? Đó là vì khi bạn gửi dữ liệu về server để tính toán, bạn mất thời gian cho các việc truyền gửi dữ liệu qua mạng, đợi server xử lí, chờ phản hồi, … trong khi đó đoạn code này lại in ra giá trị của biến result ngay khi gọi, lúc này dữ liệu vẫn chưa trả về kịp, do đó mà xuất hiện giá trị undefined.

Thế giới trước ngày Promise xuất hiện: Callback là lựa chọn

        Bạn đã nghe nhắc tới callback chưa? Nếu chưa thì bạn nên tìm hiểu qua trước một chút về callback rồi sau đó quay lại đọc tiếp bài viết này. Trước khi promise xuất hiện người ta dùng callback để xử lí các lệnh bất đồng bộ, nói một cách nôm na callback là một hàm sẽ được kích hoạt sau khi ta lấy được kết quả trả về từ một xử lí định trước. Xử lí ví dụ ở trên bằng callback sẽ trông như sau:

// add two numbers remotely, by calling an API
function addAsync (num1, num2, callback) {
    // use the famous jQuery getJSON callback API
    return $.getJSON('http://www.example.com', {
        num1: num1,
        num2: num2
    }, callback);
}

// do the calculation: 1 + 2
addAsync(1, 2, success => {
    // callback, you get result = 3 here
    const result = success;
    console.log(result);
});

         Xử lí trông có vẻ hơi phức tạp hơn một chút rồi đúng không? Hàm addAsync() chúng ta khai báo ở trên sẽ thực hiện một tính toán bất đồng bộ và nhận vào một biến là “callback”, khi tính toán xong thì hàm addAsync() sẽ thực hiện kích hoạt hàm callback. Ở phía dưới khi ta gọi hàm addAsync() để tính toán, ta đã truyền vào một hàm callback để xử lí in giá trị kết quả ra màn hình.

Vấn đề xuất hiện rồi đây!

        Nếu bạn muốn thực hiện một xử lí cộng 4 số như trên ví dụ ở đầu bài viết thì như thế nào? Có còn giống như khi viết hàm xử lí tuần tự không? Câu trả lời là không, bởi vì các xử lí tính toán là bất đồng bộ, nên ta phải tiếp tục sử dụng callback, đoạn code xử lí sẽ trông như thế này đây:

// do the calculation: 1 + 2 + 3 + 4
var num1, num2, num3;

addAsync(1, 2, success => {
   // callback, you get num1 = 3 here
   num1 = success;
   console.log("1 + 2 = " + num1);	

   addAsync(num1, 3, success => {
      // callback, you get num2 = 6 here
      num2 = success;
      console.log("1 + 2 + 3 = " + num2);

      addAsync(num2, 4, success => {
         // callback, you get num3 = 10 here
         num3 = success;
         console.log("1 + 2 + 3 + 4 = " + num3);
     });
  });
});

         Về mặt logic, ta hoàn toàn có thể xử lí được các yêu cầu đề ra ban đầu, tuy  nhiên code trông có vẻ rất rườm rà và phức tạp. Viết mã kiểu thế này thì các xử lí được lồng vào nhau, hình thành các tầng xử lí riêng biệt, nếu các đoạn code lồng vào nhau quá nhiều thì sẽ hình thành “callback hell” (địa ngục callback). Cử thử hình dung đoạn code lồng vào nhau 10 cấp, nó thực sự là “địa ngục” đó.

        Chạy demo ở đây: https://repl.it/FrqS/1

Promise xuất hiện và giải cứu chúng ta

        Với callback, rất có thể chúng ta sẽ sa chân vào “địa ngục”, chúng ta vùng vẫy đến kiệt sức và mọi thứ vẫn cứ rối tung như vậy. Nhưng rồi, Promise xuất hiện, và giải cứu chúng ta như một vị cứu tinh với lời hứa ngọt ngào đầy những sức mạnh của tình yêu thương.

         Với promise, chúng ta có thể đơn giản đoạn code trên như sau:

let resultA, resultB, resultC;

//simulate a async function
function addAsync(num1, num2) {
  return new Promise( function(resolve, reject){
    setTimeout(function(){
      resolve( num1 + num2 );
    }, 500);
  } );
}

//Execute async functions

addAsync( 1, 2 )
  .then( success1 => {
    resultA = success1;
    console.log('resultA: ' + success1);
    return addAsync( resultA, 3 );
  })
  .then( success2 => {
    resultB = success2;
    console.log('resultB: ' + success2);
    return addAsync( resultB, 4 );
  })
  .then( success3 => {
    resultC = success3;
    console.log('resultC: ' + success3);
    console.log('total (1 + 2 + 3 + 4): ' + success3);
  })
  ;

         Promise xuất hiện và giải cứu chúng ta khỏi “địa ngục callback”, promise làm “phẳng” các xử lí bất đồng bộ theo cách mà chúng ta vẫn hay viết các đoạn mã xử lí tuần tự. Từ đó, cho dù chúng ta có phải xử lí bất đồng bộ bao nhiêu lần đi chăng nữa thì code xử lí của chúng ta trông vẫn rất nhẵn nhụi, rất dễ đọc và dễ hiểu.

         Chạy demo ở đây: https://repl.it/FqVD/3

         Có thể thấy, promise thay đổi hoàn toàn cách chúng ta ứng phó với các xử lí bất đồng bộ ở phía client, làm cho việc xử lí ở phía client trở nên đơn giản hơn rất nhiều so với việc dùng callback. Nếu bạn chưa biết tới promise, có lẽ là bạn vẫn còn đang vật lộn với những callback nặng nề và phức tạp, hãy thử sử dụng promise đi, biết đâu là bạn sẽ lại tự “hứa” với lòng mình rẳng: I promise, to not use callback anymore 🙂

Vcttai.

Tham khảo:

1. Promise for dummies

2. Promise là gì

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s