Hướng dẫn Promise/Async/Await trên Javascript, NodeJS


Javascript là một ngôn ngữ đơn luồng (Single Thread) và đồng bộ (Synchronous). Nghĩa là nếu bạn đang thực thi một khối mã JavaScript trên một trang thì hiện tại sẽ không có JavaScript nào khác trên trang đó được thực thi. Tuy nhiên, nó sẽ chạy không đồng bộ trong một số trường hợp VD: Ajax. Lệnh gọi Ajax sẽ ngừng thực thi và mã khác sẽ có thể thực thi cho đến khi lệnh gọi trả về.

Callback là giải pháp đầu tiên được đưa ra của ES5 để giải quyết các vấn đề liên quan đến xử lý bất đồng bộ theo đúng trình tự mong muốn.

Bất đồng bộ trong Javascript

Xử lý bất đồng bộ là thay vì xử lý các công việc (tasks) một cách đồng bộ thì chương trình nó sẽ xử lý đồng thời các công việc cùng một lúc. Điều này sẽ làm cho chương trình chạy nhanh hơn. Nếu các task độc lập thì nó rất là hiệu quả, tuy nhiên nếu task này sử dụng kết quả của task khác mà nó lại xuất hiện vấn đề nếu nó chạy trước kia có kết quả của task kia.

Các trường hợp Javascript sẻ xử lý bất đồng bộ là:

  • Gọi Ajax
  • Xử lý Timeout
  • Query dữ liệu tới database (NodeJS)

Xử lý đồng bộ bằng Callback trong Javascript.

Để đưa chương trình Javascript về xử lý đồng bộ dữ liệu, bản ES5 đưa ra phương pháp sử dụng callback. Các bạn hãy xem ví dụ sau.

Đây là 1 đoạn code chương trình sẽ chạy không đồng bộ trong Javascript.

console.log('||== https://vinasupport.com Run Task ==||');
function taskNo1() {
    setTimeout(() => console.log("Task No 1"), 5000);
}

function taskNo2() {
    setTimeout(() => console.log("Task No 2"), 1000);
}

taskNo1();
taskNo2();

Bạn sẽ thấy

  • Task No 1 sẽ chạy trong 5 giây
  • Task No 2 sẽ chạy trong 1 giây

Nếu chương trình chạy đồng bộ như các ngôn ngữ khác thì Task No 1 sẽ xong trước Task No 2. Nhưng với Javascript thì như các bạn thấy ở bên dưới Task No 2 chạy xong trước Task No 1 do nó có thời gian chạy ngắn hơn.

Vậy để đồng bộ sử dụng Callback ta làm như sau:

// For Callback
console.log('||== https://vinasupport.com Run Task Callback ==||');
function taskNo1(taskNo2) {
    setTimeout(() => {
        console.log("Task No 1")
        taskNo2();
    }, 5000);
}

function taskNo2() {
    setTimeout(() => console.log("Task No 2"), 1000);
}

taskNo1(taskNo2);

Hoặc viết gọn lại nữa thì:

// For Callback
console.log('||== https://vinasupport.com Run Task Callback ==||');
function taskNo1(taskNo2) {
    setTimeout(() => {
        console.log("Task No 1")
        taskNo2();
    }, 5000);
}

taskNo1(function () {
    setTimeout(() => console.log("Task No 2"), 1000);
});

Có thể hiển Task No 2 giờ là 1 tham số của Task No 1 và khi kết thúc công việc của Task No 1 chúng ta gọi tiếp Task No 2 chạy.

Kết quả: Chúng ta đã đạt được kết quả mong muốn rồi đó.

Xử lý đồng bộ bằng Promise / Await / Async trong Javascript.

Xử lý Callback thật tuyệt đúng không, cho đến khi bạn có thật nhiều Task phải làm tuần tự. Hãy tưởng tượng với VD phía trên mà có 4 Task gọi Callback thì code của chúng ta như thế nào?

// For 4 Callback
console.log('||== https://vinasupport.com Run Task 4 Callback ==||');
function taskNo1(taskNo2) {
    setTimeout(() => {
        console.log("Task No 1")
        taskNo2(function (taskNo4) {
            setTimeout(() => {
                console.log("Task No 3")
                taskNo4();
            }, 3000);
        });
    }, 5000);
}

taskNo1(function (taskNo3) {
    setTimeout(() => {
        console.log("Task No 2");
        taskNo3(function () {
            setTimeout(() => console.log('Task No 4'), 4000)
        });
    }, 1000);
});

Á á giờ bạn đọc code đã thấy khó chịu chưa, mình viết code xong còn tù mù nữa là. Nhưng mà nó vẫn chạy đúng có điều khó maintain, khó mở rộng và hại đầu óc của các em Junior. Nói thế thôi chúng nó có khi còn giỏi hơn lão già admin của vinasupport.com này.

Và để khắc phục tình trạng trên Javascript trên ES6 đã đưa ra một giải pháp xử lý động bộ sử dụng Promise / Await / Async như sau:

console.log('||== https://vinasupport.com Run Task Promise ==||');
doTask = async () => {
    let result1 = await new Promise((resolve, reject) => {
        setTimeout(() => resolve("Task No 1"), 5000)
    });
    let result2 = await new Promise((resolve, reject) => {
        setTimeout(() => resolve("Task No 2"), 1000)
    });
    let result3 = await new Promise((resolve, reject) => {
        setTimeout(() => resolve("Task No 3"), 3000)
    });
    let result4 = await new Promise((resolve, reject) => {
        setTimeout(() => resolve("Task No 4"), 4000)
    });
    console.log(result1)
    console.log(result2)
    console.log(result3)
    console.log(result4)
}
doTask().then();

Kết quả:

Thế là ngon rồi đấy.

Giờ thử viết 1 đoạn code để qua mỗi 1 Task chúng ta ghép nối thêm 1 chuỗi vào duy nhất 1 biến và chỉnh sửa biến 1 cách tuần tự nhé!

console.log('||== https://vinasupport.com Run Task Promise (Completed) ==||');
doTask = async () => {
    let message = 'I am running Task: ';
    let total = 0;
    message += await new Promise((resolve, reject) => {
        setTimeout(() => resolve(" No 1 +"), 5000)
        total += 1;
    });
    message += await new Promise((resolve, reject) => {
        setTimeout(() => resolve(" No 2 +"), 1000)
        total += 1;
    });
    message += await new Promise((resolve, reject) => {
        setTimeout(() => resolve(" No 3 +"), 3000)
        total += 1;
    });
    message += await new Promise((resolve, reject) => {
        setTimeout(() => resolve(" No 4"), 4000)
        total += 1;
    });
    console.log(message)
    return total;
}
doTask().then(total => console.log("Total: " + total));

Kết quả ra sao nào?

Như các bạn đã thấy đoạn code đã chạy qua cả bốn tác một cách tuần tự và biến total đã được update thêm 1 đơn vị khi qua từng Task.

Kết luận

Lập trình Javascript thực sự là khó. Bạn cần phải hiểu rõ cơ chế hoạt động của Javascript. Để viết bài này mình đã tham khảo khá nhiều bài viết ở trên mạng mình thấy viết khá lan man. Cái quan trọng là đưa ra một ví dụ dễ hiểu và logic thì hầu như không thể hiện được. Nên mình đã viết bài này dù mất khá nhiều thời gian, hy vọng nó có thể giúp bạn hiểu và nắm được Promise. Nếu các bạn thấy hay thì hãy chia sẻ nó để cho mọi người cùng biết. Và nếu bạn thực sự tốt hãy để lại cho mình link đến cho vinasupport.com.

Nguồn: vinasupport.com

             
SHARE

Bài viết liên quan

mode_edit Bình luận của bạn

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

account_circle
web