Biết tuốt Javascript Promise trong 60 giây

Khái niệm Javascript Promise không khó, nhưng với người mới bắt đầu, có những nhầm lẫn nho nhỏ, bài viết này sẽ giúp bạn hiểu tường tận Promise là gì và sử dụng như thế nào chỉ trong 60 giây, bắt đầu thôi.

1. Promise là gì?

Promise phiên âm /ˈpräməs/ là lời hứa, vậy trong Javascript nó cũng là lời hứa? Đúng vậy, nó có liên quan, tưởng tượng bạn hứa tặng bạn gái một chiếc iphone X vào sinh nhật tuần tới, có 3 trạng thái có thể xảy ra:

  • Pending: gấu không biết là liệu bạn có tặng nó iphone X không cho nó không cho đến ngày sinh nhật nó.
  • Resolved: gấu đã được bạn tặng cho iphone X.
  • Rejected: gấu không được bạn tặng iphone X vì bạn định tặng gấu XXX :D.

Promise trong Javascript cũng có 3 trạng thái như vậy khi thực hiện một việc (lời hứa), chúng ta cùng xem đoạn code sau:

// Được XXX hay chưa
var XXXed = false;

// Hứa tặng iphone X
var willGetIphoneX = new Promise(
    function (resolve, reject) {
        if (XXXed) {
            var phone = {
                brand: 'iPhone X',
                color: 'pink'
            };
            resolve(phone);
        } else {
            var reason = new Error('Từ chối XXX nên sẽ không nhận được iPhone X');
            reject(reason);
        }
    }
);

Đoạn code trên có một biến XXXed khai báo bạn được XXX gấu hay chưa và một lời hứa willGetIphoneX, lời hứa này có trạng thái resolved nếu bạn được XXX và rejected nếu không được XXX. Dưới đây là cú pháp chuẩn khai báo một Promise.

new Promise( /* executor */ function(resolve, reject) { ... } );

executor là một hàm được truyền vào với hai tham số là resolve và reject, excutor được thực hiện ngay khi tạo ra một thực thi (implement) của Promise và truyền vào các hàm resolve và reject. Các hàm resolve và reject được gọi khi promise được hoàn thành hoặc bỏ qua. Nếu một lỗi xảy ra trong hàm executor, promise được bỏ qua, giá trị trả về của executor cũng được bỏ qua.

Trong đoạn code tiếp theo gấu của bạn có thể kiểm tra tình trạng lời hứa của bạn:

var askMeForIphoneX = function () {
    willGetIphoneX
        .then(function (fulfilled) {
            // Yeah, gấu đã nhận được iPhone X
            console.log(fulfilled);
            // { brand: 'iPhone X', color: 'pink' }
        })
        .catch(function (error) {
            // Sad, gấu không nhận được iPhone X
            console.log(error.message);
            // 'Từ chối XXX nên sẽ không nhận được iPhone X'
        });
};

askMeForIphoneX();

Như vậy, askMeForIphoneX sẽ sử dụng .then và .catch để quản lý các hành động, trong ví dụ trên chúng ta có function (fulfilled) { } trong .then sẽ được thực thi khi resolve() của Promise willGetIphoneX xảy ra và fulfilled sẽ chứa giá trị phone được truyền sang. Tương tự function (error) { } trong .catch được thực thi khi reject().

Bạn muốn xem đoạn code trên hoạt động thế nào thì vào jsfiddle.net theo link dưới, nhớ bật console lên để xem khi thay đổi giá trị XXXed xem thế nào (phím tắt bật console Ctrl + Shift + I). Các đoạn code sau cũng thế nhé.

Javascript Promise example 1

2. Thực hiện Promise theo chuỗi

Javascript Promise có thể thực hiện theo chuỗi, chúng ta tiếp tục với ví dụ ở trên, gấu của bạn lại hứa với đứa bạn thân của nó sẽ cho nó xem con iPhone X mới khi được bạn tặng. Như vậy một promise nữa được đưa ra, chúng ta viết tiếp đoạn mã cho tình huống này:

// Lời hứa thứ hai
var showReceiveIphoneX = function (phone) {
    return new Promise(
        function (resolve, reject) {
            var message = 'Ê, tao được tặng con ' + phone.brand + ' màu ' + phone.color + 'nè!';
            resolve(message);
        }
    );
};

Trong ví dụ tiếp theo này chúng ta thấy reject không được nhắc đến, do đó reject là một tùy chọn. Do chỉ cần đến resolve chúng ta có thể viết lại ngắn gọn như sau:

// Lời hứa thứ hai
var showReceiveIphoneX = function (phone) {
    var message = 'Ê, tao được tặng con ' + phone.brand + ' màu ' + phone.color + 'nè!';
    return new Promise.resolve(message);
};

Hai promise này có thể thực hiện thành chuỗi như sau:

// Gọi đến các promise
var askMeForIphoneX = function () {
    willGetIphoneX
    .then(showReceiveIphoneX) // thực hiện theo chuỗi
    .then(function (fulfilled) {
        console.log(fulfilled);
        // output: 'Ê, tao được tặng con Iphone X màu pink nè.'
    })
    .catch(function (error) {
        console.log(error.message);
        // output: 'Từ chối XXX nên sẽ không nhận được iPhone X'
    });
};

Thật dễ dàng khi xâu chuỗi các promise với nhau phải không?

Javascript Promise example 2

3. Tại sao dùng Javascript Promise?

Trước khi đến câu trả lời tại sao sử dụng Promise chúng ta cùng xem xét một số vấn đề tồn tại trước đó trong Javascript.

3.1 Đồng bộ và bất đồng bộ trong Javascript

Chúng ta xem xét ví dụ về cộng hai số trong Javascript:

function add (number_1, number_2) {
    return number_1 + number_2;
}

const total = add(1, 2);

Ví dụ này khi thực hiện ngay lập tức biến total sẽ có giá trị là 3, tuy nhiên nếu chúng ta thực hiện gọi hàm này trên một server:

const total = getAdd('http://allaravel.com/add?num1=1&num2=2');

Thì biến total sẽ có giá trị ngay lúc này là undefined, các lời gọi đến máy chủ từ xa đều cần phải chờ để thực hiện và có nhiều khi máy chủ ngừng phục vụ hoặc đường truyền chậm … cũng là những lý do mà bạn không thể biết để chờ đợi trong bao lâu. Do đó các lời gọi đến máy chủ từ xa phải hoạt động theo kiểu bất đồng bộ (asynchronous), trước khi có Promise để viết code kiểu bất đồng bộ sử dụng hàm gọi ngược (callback function). Quay lại ví dụ gọi hàm cộng hai số từ xa:

function addAsync (number_1, number_2, callback) {
    return $.get('http://allaravel.com/add', {
        number_1: number_1,
        number_2: number_2
    }, callback);
}

addAsync(1, 2, success => {
    // callback
    const total = success; // Ở đây total sẽ nhận giá trị là 3
});

Trong đoạn ví dụ trên chúng ta thấy callback function cũng rất ok, đâu có vấn đề gì mà cần đến Promise đâu?

3.2 Promise để xử lý callback hell

Quay lại ví dụ trong 3.1 nếu chúng ta muốn thực hiện cộng nhiều số với nhau thì cả code đồng bộ và bất đồng bộ sẽ thế nào? Với code đồng bộ:

let total_A, total_B, total_C;

 function add (number_1, number_2) {
    return number_1 + number_2;
}

total_A = add(1, 2); // total_A có giá trị là 3 ngay lập tức
total_B = add(total_A, 3); // total_B có giá trị là 6 ngay lập tức
total_C = add(total_B, 4); // total_C có giá trị là 10 ngay lập tức

console.log('Tổng của 1 + 2 + 3 + 4 = ' + total_C);

Ví dụ này sẽ thế nào nếu phải cộng các số bằng cách gọi đến các máy chủ từ xa:

let total_A, total_B, total_C;

function addAsync (number_1, number_2, callback) {
    return $.get('http://allaravel.com/add', {
        number_1: number_1,
        number_2: number_2
    }, callback);
}

addAsync(1, 2, success => {
    // callback 1
    total_A = success; // total_A có giá trị là 3 tại đây

    addAsync(total_A, 3, success => {
        // callback 2
        total_B = success; // total_B có giá trị là 6 tại đây

        addAsync(total_B, 4, success => {
            // callback 3
            total_C = success; // total_C có giá trị là 10 tại đây
            console.log('Tổng của 1 + 2 + 3 + 4 = ' + total_C);
        });
    });
});

Tưởng tượng nếu phải cộng 10 số thì các cấu trúc lồng nhau sẽ càng nhiều nó được gọi là callback hell. Để loại bỏ callback hell, các nhà phát triển đã nghĩ ra Promise, chúng ta cùng xem ví dụ trên đây được thực hiện với Javascript Promise:

let total_A, total_B, total_C;

function addAsync(number_1, number_2) {
    return Promise.resolve(number_1 + number_2);
}

addAsync(1, 2)
.then(success => {
    total_A = success;
    return total_A;
})
.then(success => addAsync(success, 3))
.then(success => {
    total_B = success;
    return total_B;
})
.then(success => addAsync(success, 4))
.then(success => {
    total_C = success;
    return total_C;
})
.then(success => {
    console.log('Tổng của 1 + 2 + 3 + 4 = ' + success);
});

Với Promise chúng ta đã “làm phẳng” được callback hell với .then.

 

4. Lời kết

Đến đây chắc chắn tôi sẽ nhận được rất nhiều lời chửi thầm, mịa tiêu đề thì kêu tìm hiểu mất 60 giây mà đọc mất đúng 1 phút :D, để vậy cho anh em có tâm lý đơn giản hóa trước khi nạp thêm kiến thức vào đầu thôi. Javascript Promise đúng là một cái gì đó khó hiểu với những người mới, tôi cũng hy vọng qua bài viết này chúng ta hiểu rõ hơn về Promise. Cũng có những câu hỏi như Promise được hỗ trợ trong những phiên bản nào của Javascript hay ES, bạn hoàn toàn yên tâm. Các trình duyệt hiện nay đều hỗ trợ ES6 và trong phiên bản này, Promise được hỗ trợ hoàn toàn.

2 thoughts on “Biết tuốt Javascript Promise trong 60 giây

  1. mình không hiểu đoạn này lắm. addAsync(1, 2, success => {
    // callback
    const total = success; // Ở đây total sẽ nhận giá trị là 3
    });
    success là gì, sao tự dưng lại gọi ở đây. mình thay success bằng abc thì có đúng không??

Add Comment