+1

PROMISE

Giới thiệu

Tại sao cần dùng promise trong js, khi đó ta cần hiểu về 1 số định nghĩa về syncasync trong js

  • Js là một ngôn ngữ đồng bộ sync
  • Nhưng nó cũng có 1 số hàm thao tác bất đồng bộ async

VD: setTimeout, setInterval, fetch, XMLHttp Request, file reading, request animation frame

Pain (nỗi đau)

Trong Naruto - hoạt hình Nhật Bản, nhân vật Pain có câu nói: " Những kẻ không hề biết đến nỗi đau thực sự sẽ không bao giờ biết đến hòa bình thực sự." 😃

Thế nên để hiểu rõ về khái niệm promise thì khi không có promise thì khi giải quyết vấn đề (pain) gì thì ta cần dùng promise?

Vấn đề này thường được gọi tên là Callback hell hay JavaScript Pyramid of Doom

Khi nhiều callback lồng nhau, các công việc cần giải quyết phụ thuộc vào nhau, output việc 1 là input cho việc 2,.....Làm code trở nên khó đọc, khó hiểu và khó bảo trì.

image.png

Khi đó dùng promise js hỗ trợ xử lý các thao tác bất đồng bộ thay cho callback.

Concept

Cú pháp:

    var promise = new Promise(
        function(resolve, reject) {
            //logic 
            // resolve: success !
            // reject: fail
            reject('co loi');
        }
        
        promise
            .then(function() {
                //logic success
            })
            .catch(function(error) {
                console.log(error)
            })
             .finally(function() {
                console.log('proccess done')
            })
  • Phần định nghĩa promise sẽ định nghĩa trả về resolve(), reject('co loi') hoặc không gì cả tương ứng với 3 trạng thái bên dưới

  • Khi thực thi :

    • Trả về resolve() => thì phần thực thi sẽ chạy vào .then
    • Trả về reject() => phần thực thi sẽ chạy vào .catch
    • Và có trả về gì đi nữa thì thực thi cũng sẽ chạy vào .finally
  • Khi định nghĩa promise có 3** trạng thái**:

    • Pending: promise không resolve hay reject
    • Fulfilled: resolve
    • Rejected: reject

Chain

Tính chất chuỗi của promise

    var promise = new Promise(
        function(resolve, reject) {
            resolve();
        }
        
        promise
            .then(function() {
                return 1
            })
            .then(function(data) {
                console.log(data);
                return data + 1;
            })
            .then(function(data) {
                console.log(data);
            })
            .catch(function(error) {
                console.log(error)
            })
             .finally(function() {
                console.log('proccess done')
            })

trong then hay catch, nếu không return ra 1 promise mới thì then thứ 2 sẽ được chạy luôn

Cú pháp có vẻ dễ hiểu và chạy tuần tự từ trên xuống dưới, và không gặp phải vấn đề về callback hell, Nhưng nếu return ra 1 promise mới thì phải chờ promise mới đó chạy xong thì mới chạy tiếp then

let promise = new Promise(
    function(resolve, reject) {
        resolve();
    })
    
    promise
        .then(function() {
           return new Promise(function(resolve, reject) {
            setTimeout(() => {
                resolve([1, 2, 3])
            }, 3000)
           });
        })
        .then(function (params) {
            console.log(params)
        })
        .catch(function (err) {
            console.log(err);
        })
        .finally(function () {
            console.log('done!');
        })

Từ .then thứ 2, sau khi return ra 1 promise ở .then 1 thì .then 2 được hiểu là 1 hành động .then của promise mới vừa return ở trên 😃

Promise method (resolve, reject, all)

promise.resolve

Khi một vào trường hợp có dữ liệu sẵn và muốn sử dụng promise chắc chắn thành công có thể sử dụng

    let promise = Promise.resolve('success')

promise
  .then((res) => {
    console.log('res');
  })

promise.reject

Tương tự thế muốn sử dụng promise chắc chắn thất bại có thể sử dụng

let promise = Promise.reject('fauilt')

promise
  .catch((err) => {
    console.log(err);
  })

promise.all

Khi có nhiều promise, có thể chạy các promise này xong xong để đỡ tốn thời gian, cũng như các promise này phải thành công hết thì mới nên dùng promise.all

let promise1 = new Promise((resolve, reject) => {
  setTimeout(function() {
    resolve([1])
  }, 5000)
})
let promise2 = new Promise((resolve, reject) => {
  setTimeout(function() {
    resolve([2, 3])
  }, 3000)
})
let promise = Promise.all([promise1, promise2])

promise
  .then(function(result) {
    let result1 = result[0];
    let result2 = result[1];
    console.log(result1.concat(result2))
  })

Example

Để làm 1 ví dụ show ra comment đơn giản dùng promise trong thực tế:

  • input:

    • api: list users
    • api: list comment
  • output: danh sách comment gồm: tên user+nội dung comment

Dữ liệu mảng đầu vào


//input
let users = [
    {
        id: 1,
        name: 'John',
    },
    {
        id: 2,
        name: 'smiley',
    },
    {
        id: 3,
        name: 'kate',
    }
];

let comments = [
    {
        id: 1,
        user_id: 1,
        content: 'hom nay ban the nao smiley?',
    },
    {
        id: 2,
        user_id: 2,
        content: 'Toi on nhe, con ban thi sao John?',
    },
    {
        id: 3,
        user_id: 1,
        content: 'Toi cung ok hehe!',
    }
];

  • nội dung html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>hello world</h1>
    <ul id="commentBlock"></ul>

    <script src="promise.js"></script>
</body>
</html>
  • js xử lý
//lay cac user theo mảng userIds
function getUserById(users, userIds) {
    return new Promise(function(resolve) {
        let result = users.filter(function(user) {
            return userIds.includes(user.id);
        });

         setTimeout(function() {
            resolve(result);
         }, 1000);
    });
}

// Tạo promise trả về comment
function getComments() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(comments)
        }, 1000)
    });
}

// Thực hiện return ra html comment
getComments()
    .then(function(comments) {
        //lấy mảng userIds từ danh sách comment
        let userIds = comments.map(function(comments) {
            return comments.user_id;
        });
        
        //return mảng đối tượng gồm chỉ gồm những user có comment và comment đó
        return getUserById(users, userIds)
            .then(function(dataUsers) {
                return {
                    users: dataUsers,
                    comments: comments
                };
            });
    })
    .then(function(data) {
        // lấy elements
        let commentBlock = document.querySelector("#commentBlock");

        let html = '';
        //lặp mảng lấy nội dung comments
        data.comments.forEach(comment => {
            //lấy ra user có thuộc comment đang lặp
            let user = users.find(user => user.id === comment.user_id);

            html += `<li>${user.name}: ${comment.content}</li>`;
        });

        commentBlock.innerHTML = html; 
    });

và đây là kết quả render

image.png


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí