Latest Post

Tăng thứ hạng và truy cập tự nhiên với 10 phương pháp SEO hay nhất Kiếm Tiền Online (mmo): Khái Niệm và Các Hình Thức Phổ Biến

JavaScript là một ngôn ngữ đơn luồng (Single Thread) và đồng bộ (Synchronous), nghĩa là khi bạn thực thi một đoạn mã JavaScript trên một trang, không có bất kỳ mã JavaScript khác trên trang đó được thực thi cùng lúc. Tuy nhiên, có một số trường hợp mà JavaScript có thể chạy không đồng bộ, ví dụ như trong trường hợp sử dụng Ajax. Khi bạn gọi một yêu cầu Ajax, nó sẽ không chờ đợi cho đến khi yêu cầu hoàn thành mà sẽ tiếp tục thực thi mã khác cho đến khi yêu cầu Ajax trả về kết quả.

Để xử lý các tình huống bất đồng bộ và đảm bảo rằng mã được thực thi theo đúng trình tự mong muốn, ES5 đã giới thiệu khái niệm Callback. Callback là một giải pháp đầu tiên để xử lý xong trình tự trong các tình huống bất đồng bộ.

Bất đồng bộ trong Javascript

Xử lý bất đồng bộ, thay vì thực hiện các nhiệm vụ một cách tuần tự, cho phép chương trình xử lý nhiều nhiệm vụ cùng một lúc. Điều này giúp tăng tốc độ thực thi của chương trình. Nếu các nhiệm vụ là độc lập, tiến trình này rất hiệu quả. Tuy nhiên, nếu một nhiệm vụ cần sử dụng kết quả từ một nhiệm vụ khác, sự xử lý bất đồng bộ có thể dẫn đến vấn đề nếu nhiệm vụ đó chạy trước khi nhiệm vụ khác hoàn thành và cung cấp kết quả cần thiết.

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

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

Trong trường hợp chương trình chạy đồng bộ, thứ tự hoàn thành Task No 1 sẽ luôn nhanh hơn Task No 2, giống như nhiều ngôn ngữ lập trình khác. Tuy nhiên, với JavaScript, chúng ta thấy rằng Task No 2 hoàn thành trước Task No 1. Điều này xảy ra bởi vì Task No 2 có thời gian thực thi ngắn hơn, cho phép nó hoàn thành trước.

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);
});

Sao nó khó chịu thế này, bạn đọc code rồi có thấy khó chịu không? Mình viết xong code rồi nhưng mà nó trông tởm lợm vãi chưởng. Vẫn chạy đúng, thậm chí còn hoạt động tốt, nhưng cứ như mớ lộn, cả bãi đổ rác vậy. Không phải ngại thật, chứ cái đống code này đang từ từ hủy hoại tâm hồn của các đứa trẻ đang tập làm dev nữa kìa. Thôi mà, thằng em nó cũng cảm ơn mình đã viết đấy, có khi còn hơn thằng admin già cỏ này trên blog.

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à một thách thức. Để thành thạo trong JavaScript, bạn cần hiểu rõ nguyên tắc hoạt động của ngôn ngữ này. Trong quá trình viết bài này, tôi đã tham khảo nhiều nguồn trực tuyến, và thấy rằng nhiều bài viết thường khá phức tạp và khó hiểu. Điều quan trọng là phải trình bày ví dụ một cách dễ hiểu và logic để giúp mọi người thấu hiểu.

Tác giả đã dành nhiều thời gian để viết bài này với hy vọng rằng nó sẽ giúp bạn hiểu sâu hơn về Promise trong JavaScript. Nếu bạn thấy bài viết hữu ích, hãy chia sẻ nó để mọi người có cơ hội học hỏi và nắm vững kiến thức này.

Trả lời

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 *