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

        Javascript là một ngôn ngữ rất hữu dụng nhưng lại hết sức kì quặc. Khi bạn làm việc với Javascript, một trong những điều khó khăn nhất mà bạn sẽ phải đối mặt đó là xử lí các lệnh bất đồng bộ (Asynchronous processing). Tính chất này của Javascript được sinh ra bởi vì hầu hết mã JS được xử lí ở phía client – tách biệt với server – do đó không phải bất cứ xử lí dữ liệu nào cũng được đồng bộ ngay xuống CSDL của ứng dụng.

Xử lí bất đồng bộ là gì?

        Không giống như các ngôn ngữ phía server – nơi dữ liệu luôn được lấy và xử lí một cách nhanh chóng – Javascript là ngôn ngữ được xử lí hầu hết ở phía client, đồng nghĩa với việc mỗi lần bạn muốn lấy một dữ liệu nào đó bạn phải gửi yêu cầu (request) về phía server và phải đợi server phản hồi. Trong quá trình đợi server trả về dữ liệu, có những xử lí cần phải được tiếp tục chạy, một số xử lí khác thì cần phải đợi cho tới khi dữ liệu được phản hồi về client mới có thể kích hoạt, do ta không biết được chính xác khi nào thì dữ liệu đã được xử lí ở client sẽ đồng bộ với phía server, vì vậy ta gọi các xử lí có tính chất này là: xử lí bất đồng bộ (giữa client và server).

Vậy thì Promise là gì?

         Để xử lí các lệnh bất đồng bộ, ta có thể dùng cơ chế gọi hàm callback, tuy nhiên không phải lúc nào ta cũng có thể áp dụng được kĩ thuật này, và không phải lúc nào kĩ thuật này cũng tốt. Một trong những kĩ thuật khác mà Javascript hỗ trợ đó là sử dụng Promise.

       Sắp tới valentine rồi, mình sẽ trình bày theo phong cách tình cảm lâm li cho mọi người dễ hình dung nhé. Hình dung một cách ngắn gọn:

Bạn tỏ tình với một cô gái, và cô ấy hứa (promise) là sẽ trả lời bạn trong một ngày nào đó vào tuần sau.

        Đây là một promise điển hình, một promise sẽ có 3 trạng thái (state) như sau:

  • Promise đang pending: bạn sẽ không biết được kết quả cho tới khi cô ấy trả lời vào tuần sau.
  • Promise được trả lời và chấp nhận – trạng thái resolved: cô ấy cũng yêu bạn, cô ấy trả lời bạn rằng cô ấy cũng yêu bạn và đồng ý làm người yêu của bạn. (quá tuyệt vời)
  • Promise được trả lời và từ chối – trạng thái rejected: cô ấy trả lời bạn và nói rằng cho ấy chỉ xem bạn như một người anh trai tốt.

        Chúng ta cùng xem việc tạo và sử dụng một promise trong Javascript thì như thế nào nhé.

Tạo 1 promise

Chuyển thể từ ngôn ngữ nói sang Javascript, giả sử tâm trạng của cô ấy sẽ quyết định tới câu trả lời:

/* ES5 */
var isSheHappy = false;

// Promise
var willBeCouple = new Promise(
    function (resolve, reject) {
        if (isSheHappy) {
            var answer = {feedback: "I accept to be your girlfriend"};
            resolve(answer); // fulfilled
        } else {
            var reason = new Error('I do not love you');
            reject(reason); // reject
        }
    }
);

Đoạn code trên đã khá rõ ràng:

  • Biến boolean isSheHappy để xác định cô ấy có vui khi bạn tỏ tình hay không.
  • Bạn có một promise là willBeCouple, biến này có thể có trạng thái là resolved (nếu cô ấy đồng ý lời tỏ tình) hoặc rejected (nếu cô ấy từ chối bạn).

Nói tóm lại, việc khai báo sử dụng một promise sẽ có cấu trúc như sau:

// promise syntax look like this
new Promise(/* executor*/ function (resolve, reject) { ... } );

        Trong trường hợp xử lí kết quả là thành công, hàm resolve() sẽ được chạy. Ngược lại, nếu kiểm tra và kết quả là fail, thì hàm reject() sẽ được kích hoạt. Bạn có thể đọc đoạn code phần ví dụ ở trên để hình dung cách gọi một promise.

Xử lí một promise – Chạy tiếp giả định lúc nãy

Ở phía trên, bạn đã hình dung được một promise nó là như thế nào, bây giờ chạy thử xem sao nhé:

/* ES5 */
...

// call our promise
var askYourGirlFriend = function () {
    willBeCouple
        .then(function (fulfilled) {
            // yeah, she accepted your proposal
            console.log(fulfilled);
            // output: {feedback: 'I accept to be your girlfriend'}
         })
         .catch(function (error) {
             // oops, she refused it
             console.log(error.message);
             // output: 'I do not love you'
         });
};

askYourGirlFriend();

Đoạn code trên có gì nào?

  • Bạn khai báo một hàm là askYourGirlFriend(), trong hàm này cũng ta sẽ xử lí lệnh willBeCouple.
  • Để xử lí kết quả promise, chúng ta dùng then()catch() trong hai trường hợp thành công và thất bại tương ứng.
  • Tham số fullfilled khi xử lí then() chính là kết quả trả về khi promise được resolved, trong trường hợp này đó chính là biến answer. Tương tự, tham số error trong xử lí catch() chính là kế quả trả về khi promise bị rejected, trong trường hợp này là biến reason.

Promise có tính chất chainable

        Một tính chất cũng khá hữu dụng của promise là nó có thể chainable, tức là có thể xử lí nhiều promise nối tiếp nhau. (xin lỗi mình không biết dịch ra tiếng việt thế nào, thôi thì để tiếng anh cho dễ hiểu).

Hình dung như sau: bạn hứa sẽ nói với bạn bè về kết quả lần tỏ tình của mình. Việc này được triển khai như sau, chúng ta sẽ tạo thêm một promise nữa:

// 2nd promise
var showOff = function (answer) {
    return new Promise(
        function (resolve, reject) {
           var message = 'Hey friend, she accepted. This is the result: '
             + answer.feedback;
           resolve(message);
        }
    );
};

Chú ý: ở lần này ta không gọi reject, bởi vì nó có thể không có.

       Bây giờ chúng ta sẽ chain promise, chúng ta sửa lại hàm askYourGirlFriend() một chút, ý định của chúng ta bây giờ là khi cô ấy trả lời là mình sẽ báo kết quả luôn cho bạn bè biết, việc đó được triển khai như sau:

// call our promise
var askYourGirlFriend = function () {
    willBeCouple
        .then(showOff) // chain it here
        .then(function (fulfilled) {
           console.log(fulfilled);
           // output: 'Hey friend, she accepted. This is the result: I accept to be your girlfriend.'
        })
        .catch(function (error) {
           // oops, she refused it
           console.log(error.message);
           // output: 'I do not love you'
        });
};

Đơn giản phải không nào!

Vậy thì promise có tính asynchronous ở chỗ nào?

Promise có tính asynchronous, để chứng minh điều đó, chúng ta thử nghiệm như sau:

// call our promise
var askYourGirlFriend = function () {
    console.log("before promise!");

    willBeCouple
       .then(showOff) // chain it here
       .then(function (fulfilled) {
          console.log(fulfilled);
          // output: 'Hey friend, she accepted. This is the result: I accept to be your girlfriend.'
       })
       .catch(function (error) {
           // oops, she refused it
           console.log(error.message);
           // output: 'I do not love you'
       });

    console.log("after promise!");
};

askYourGirlFriend();

         Thử đoán xem kết quả là gì nào? Nhiều người sẽ nghĩ là kết quả sẽ thế này:

before promise!
Hey friend, she accepted. This is the result: I accept to be your girlfriend.'
after promise!

        Nhưng … kết quả ở trên là SAI nhé, kết quả đúng phải là:

before promise!
after promise!
Hey friend, she accepted. This is the result: I accept to be your girlfriend.'

        Lí giải cho điều này ư? Đơn giản thôi, bạn hãy hình dung: Bạn đâu có “bất động” trong lúc bạn đang chờ câu trả lời của cô người yêu phải không nào? Code cũng thế, các xử lí sẽ tiếp tục được chạy mà không phải chờ kết quả (đời vẫn trôi đi khi bạn đợi câu trả lời) , bất cứ xử lí nào bạn muốn làm sau khi có kết quả của promise thì bạn đặt nó vào trong khối lệnh then(). Đây là một kiểu xử lí mà chúng ta gọi là “bất đồng bộ” (asynchronous).

 (Còn tiếp)

Trong phần tới mình sẽ phân tích các lợi thế của promise, và lí do gì nó lại "ngọt ngào", các bạn nhớ đón xem nhé.
Source code demo: https://repl.it/FqUc/0

Vcttai

Tham khảo:

1. Promise for dummies

2. Promise là gì

2 thoughts on “[Javascript] Promise – Lời hứa ngọt ngào (P1)

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

  2. Pingback: [ Javascript ] Callback function và Higher-order function trong Javascript – Những dòng code vui

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