[Javascript] Promise - Lời hứa ngọt ngào (P.2)
Javascript
246
async
8
White

Tấn Tài Vũ viết ngày 04/09/2017

Mình cũng viết ở: [Javascript] Promise – Lời hứa ngọt ngào (P2)

Ở 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” đó.

Xem demo ở ĐÂY

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 sức mạnh.

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.

Xem demo ở ĐÂY

Lưu ý: Nếu bạn viết Promise không khéo thì code xử lí vẫn sẽ "phân tầng". Nếu điều đó xảy ra, bạn nên biết rằng mình đã sử dụng Promise sai cách. Chi tết xem thêm ở ĐÂY

Ta thậm chí còn có thể chạy song song các lệnh Async với Promise

Không chỉ cung cấp cho ta một cách thức chạy bất đồng bộ để thay thế callback, Promise còn cho phép ta chạy một lần nhiều xử lí song song, với một cách thức gọi hàm rất đơn giản - một điều mà trước đây với callback ta rất khó để thực hiện:

//Async function using Promise
function testPromiseNomal(name) {
  var time = Math.round(Math.random() * 2000);

  return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve("Hello " + name);
        }, time);
  });
}


// Test promise
var names = ["John", "Peter", "Michael"];

// if the reject function is detected, the process will be stopped immediately
var names_mapped = names.map(function(name) {
    return testPromiseNomal(name);
});

Promise.all(names_mapped).then(function(response) {
    console.log(response);
});

Để chạy các xử lí bất đồng bộ một cách song song, chúng ta sử dụng hàm map của Array để trả về một mảng các Promises. Câu lệnh Promise.all() sẽ chờ cho đến khi tất cả các kết quả của Promise được resolved rồi mới thực hiện khối lệnh .then() sau đó. Rất đơn giản phải không nào!

Xem Demo ở ĐÂY

Kết

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 :wink:

vcttai 04-09-2017

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Tấn Tài Vũ

11 bài viết.
91 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
28 6
Nguồn: (Link) Nhắc lại về các kiểu dữ liệu trong Javascript, ta có 5 kiểu dữ liệu cơ bản và 1 kiểu dữ liệu phức hợp. 5 kiểu dữ liệu cơ bản bao gồm...
Tấn Tài Vũ viết 1 năm trước
28 6
White
25 9
Note: Bài viết nguồn ở đây: (Link) Con trỏ this có lẽ là một khái niệm không mấy xa lạ trong lập trình hướng đối tượng, nó là một thể hiện cho đố...
Tấn Tài Vũ viết 1 năm trước
25 9
White
23 1
Mấy hôm nay có dịp ôn lại về HTTPS, mặc dù đã có nhiều tài liệu nói về giao thức này nhưng hầu hết chúng thường được mô tả bằng ngôn ngữ kĩ thuật t...
Tấn Tài Vũ viết 3 tháng trước
23 1
Bài viết liên quan
White
43 8
Tăng sức mạnh cho javascript với lodash Lần này mình sẽ giới thiệu 1 thư viện javascript vô cùng bá đạo có tên là "lodash]1]", có thể nói nó là LI...
Huy Hoàng Phạm viết hơn 2 năm trước
43 8
White
27 8
Lâu không post gì muốn viết một bài dài dài về js cơ mà đau đầu quá viết mãi không xong, thôi post bài ngắn vậy :smiley: Lấy screen size ở đây tôi...
Hoàng Duy viết gần 3 năm trước
27 8
White
6 0
Có bao giờ bạn thắc mắc, chuyện gì thực sự diễn ra khi chúng ta gõ một địa chỉ trang web (ví dụ: (Link)) lên trình duyệt và nhấn Enter? Đầu tiên, t...
Lam Pham viết 6 tháng trước
6 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
11 bài viết.
91 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!