JavaScript bất đồng bộ: Callback, Promise và Async/Await

JavaScript bất đồng bộ: Callback, Promise và Async/Await

Tìm hiểu JavaScript bất đồng bộ từ cơ bản đến nâng cao với Callback, Promise và Async/Await. Nắm vững cách hoạt động, Event Loop, xử lý lỗi, so sánh từng phương pháp và các ví dụ thực tế dễ hiểu, chi tiết như nào hãy cùng chúng tôi phân tích:

JavaScript là ngôn ngữ lập trình hoạt động theo mô hình đơn luồng (Single Thread), nghĩa là tại một thời điểm chỉ thực hiện một tác vụ. Tuy nhiên, trong thực tế, các ứng dụng web thường phải tải dữ liệu từ máy chủ, đọc tệp, gọi API, xử lý hình ảnh hoặc chờ người dùng thao tác. Nếu JavaScript thực hiện các công việc này theo kiểu đồng bộ, toàn bộ giao diện sẽ bị "đứng" cho đến khi tác vụ hoàn tất.

Để giải quyết vấn đề đó, JavaScript sử dụng lập trình bất đồng bộ (Asynchronous Programming). Đây là một trong những kiến thức quan trọng nhất đối với bất kỳ lập trình viên JavaScript nào, từ người mới học đến lập trình viên chuyên nghiệp.

Trong bài viết này, Web Mới sẽ giúp bạn hiểu rõ bản chất của lập trình bất đồng bộ, cách hoạt động của Callback, PromiseAsync/Await, đồng thời phân tích ưu điểm, hạn chế cũng như thời điểm nên sử dụng từng phương pháp.

JavaScript bất đồng bộ: Callback, Promise và Async/Await

Lập trình bất đồng bộ là gì?

Bất đồng bộ là cơ chế cho phép JavaScript không phải chờ một tác vụ hoàn thành mới tiếp tục chạy các đoạn mã phía sau. Khi gặp một tác vụ mất thời gian như gọi API hoặc tải dữ liệu, JavaScript sẽ giao việc đó cho môi trường thực thi (trình duyệt hoặc Node.js), sau đó tiếp tục thực hiện các lệnh khác.

Khi tác vụ hoàn thành, kết quả sẽ được đưa trở lại hàng đợi và JavaScript tiếp tục xử lý.

Nhờ cơ chế này, ứng dụng luôn phản hồi nhanh, giao diện không bị treo và trải nghiệm người dùng được cải thiện đáng kể.

Ví dụ về xử lý đồng bộ

Đoạn mã dưới đây được thực hiện lần lượt từ trên xuống dưới.

console.log("Bắt đầu");

console.log("Đang xử lý");

console.log("Kết thúc");

Kết quả:

Bắt đầu
Đang xử lý
Kết thúc

Mọi lệnh đều phải chạy xong trước khi chuyển sang lệnh tiếp theo.

Ví dụ về xử lý bất đồng bộ

Hàm setTimeout() sẽ trì hoãn việc thực thi một đoạn mã.

console.log("Bắt đầu");

setTimeout(() => {
    console.log("Đã hết 2 giây");
}, 2000);

console.log("Kết thúc");

Kết quả:

Bắt đầu
Kết thúc
Đã hết 2 giây

Mặc dù setTimeout() được gọi trước, nhưng JavaScript không chờ đủ 2 giây mà tiếp tục thực hiện dòng tiếp theo.

Tại sao JavaScript cần bất đồng bộ?

Hầu hết các ứng dụng hiện đại đều phải làm việc với nhiều tác vụ mất thời gian.

  • Gửi và nhận dữ liệu từ API.
  • Tải hình ảnh hoặc video.
  • Đọc và ghi tệp.
  • Kết nối cơ sở dữ liệu.
  • Đăng nhập người dùng.
  • Thanh toán trực tuyến.
  • Upload file dung lượng lớn.
  • Chờ người dùng thao tác.

Nếu mọi tác vụ đều chạy đồng bộ, toàn bộ giao diện sẽ bị khóa cho đến khi hoàn thành, gây trải nghiệm rất kém.

Callback là gì?

Callback là một hàm được truyền vào làm tham số của một hàm khác và sẽ được gọi khi tác vụ hoàn tất.

Đây là phương pháp đầu tiên JavaScript sử dụng để xử lý bất đồng bộ.

Ví dụ Callback đơn giản

function xinChao(callback) {
    console.log("Xin chào");
    callback();
}

xinChao(function () {
    console.log("Web Mới");
});

Kết quả:

Xin chào
Web Mới

Trong ví dụ trên, hàm được truyền vào sẽ chỉ được thực hiện sau khi hàm xinChao() chạy xong.

Callback với setTimeout()

console.log("Bắt đầu");

setTimeout(function () {
    console.log("Đã tải dữ liệu");
}, 3000);

console.log("Tiếp tục xử lý");

Kết quả:

Bắt đầu
Tiếp tục xử lý
Đã tải dữ liệu

Đây là ví dụ điển hình về Callback trong lập trình bất đồng bộ.

Nhược điểm của Callback

Khi chương trình trở nên phức tạp, các Callback thường lồng vào nhau nhiều tầng.

dangNhap(function () {

    layThongTin(function () {

        layDanhSachSanPham(function () {

            thanhToan(function () {

                guiEmail(function () {

                    console.log("Hoàn thành");

                });

            });

        });

    });

});

Kiểu viết này thường được gọi là Callback Hell.

Những vấn đề thường gặp:

  • Khó đọc.
  • Khó bảo trì.
  • Khó mở rộng.
  • Khó xử lý lỗi.
  • Dễ gây nhầm lẫn khi có nhiều tầng lồng nhau.

Chính vì vậy, JavaScript đã giới thiệu Promise để giải quyết hạn chế này.

Promise là gì?

Promise là một đối tượng (Object) đại diện cho kết quả của một tác vụ bất đồng bộ trong tương lai. Thay vì phải truyền nhiều Callback lồng nhau, Promise cho phép xây dựng luồng xử lý theo từng bước rõ ràng, dễ đọc và dễ bảo trì hơn.

Có thể hiểu đơn giản, Promise giống như một "lời hứa". Khi một tác vụ được bắt đầu, JavaScript chưa biết kết quả ngay lập tức. Promise sẽ giữ trạng thái của tác vụ đó và thông báo khi hoàn thành hoặc xảy ra lỗi.

Hiện nay, Promise là nền tảng của hầu hết các API bất đồng bộ hiện đại trong JavaScript và cũng là cơ sở để xây dựng Async/Await.

Ba trạng thái của Promise

Một Promise luôn tồn tại ở một trong ba trạng thái sau.

Trạng thái Ý nghĩa
Pending Đang chờ xử lý, chưa có kết quả.
Fulfilled Tác vụ hoàn thành thành công và trả về dữ liệu.
Rejected Tác vụ thất bại hoặc xảy ra lỗi.

Điểm quan trọng là một Promise chỉ chuyển trạng thái một lần duy nhất. Sau khi đã thành công hoặc thất bại thì trạng thái đó sẽ không thay đổi nữa.

Cách tạo một Promise

Một Promise được tạo bằng từ khóa new Promise().

const promise = new Promise((resolve, reject) => {

    const thanhCong = true;

    if (thanhCong) {
        resolve("Đã xử lý thành công");
    } else {
        reject("Có lỗi xảy ra");
    }

});

Trong đó:

  • resolve() dùng để thông báo Promise hoàn thành thành công.
  • reject() dùng để thông báo Promise thất bại.

Nhận kết quả từ Promise

Sau khi Promise hoàn thành, có thể sử dụng .then() để nhận dữ liệu trả về.

const promise = new Promise((resolve) => {

    resolve("Xin chào Web Mới");

});

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

Kết quả:

Xin chào Web Mới

Phương thức .then() chỉ được gọi khi Promise thành công.

Xử lý lỗi bằng catch()

Nếu Promise bị từ chối, phương thức .catch() sẽ nhận lỗi.

const promise = new Promise((resolve, reject) => {

    reject("Không thể kết nối máy chủ");

});

promise
.then((data) => {
    console.log(data);
})
.catch((error) => {
    console.log(error);
});

Kết quả:

Không thể kết nối máy chủ

Việc tách riêng phần xử lý lỗi giúp chương trình rõ ràng hơn rất nhiều so với Callback.

finally() hoạt động như thế nào?

Có những trường hợp bạn muốn thực hiện một công việc bất kể Promise thành công hay thất bại, ví dụ:

  • Tắt biểu tượng đang tải.
  • Ẩn cửa sổ chờ.
  • Đóng kết nối.
  • Giải phóng tài nguyên.

Khi đó có thể sử dụng .finally().

promise
.then((data) => {
    console.log(data);
})
.catch((error) => {
    console.log(error);
})
.finally(() => {
    console.log("Kết thúc xử lý");
});

Dù Promise thành công hay thất bại thì đoạn mã trong .finally() vẫn luôn được thực hiện.

Chuỗi Promise (Promise Chaining)

Một ưu điểm lớn của Promise là có thể nối nhiều bước xử lý liên tiếp mà không cần lồng nhiều hàm.

Promise.resolve(5)

.then((value) => {
    return value * 2;
})

.then((value) => {
    return value + 10;
})

.then((value) => {
    console.log(value);
});

Kết quả:

20

Mỗi .then() sẽ nhận giá trị trả về từ bước trước, giúp luồng xử lý trở nên mạch lạc và dễ theo dõi.

Promise giúp giải quyết Callback Hell như thế nào?

Giả sử cần thực hiện lần lượt các bước sau:

  1. Đăng nhập.
  2. Lấy thông tin người dùng.
  3. Tải danh sách bài viết.
  4. Hiển thị dữ liệu.

Nếu dùng Callback, mã nguồn thường bị lồng nhiều tầng.

dangNhap(function () {

    layThongTin(function () {

        layDanhSach(function () {

            hienThi();

        });

    });

});

Khi chuyển sang Promise, luồng xử lý sẽ rõ ràng hơn.

dangNhap()

.then(layThongTin)

.then(layDanhSach)

.then(hienThi)

.catch((error) => {
    console.log(error);
});

Mỗi bước chỉ tập trung vào một nhiệm vụ, giúp việc đọc, sửa lỗi và mở rộng chương trình trở nên đơn giản hơn.

Kết hợp nhiều Promise

JavaScript cung cấp nhiều phương thức để xử lý nhiều Promise cùng lúc. Việc lựa chọn đúng phương thức sẽ giúp tối ưu hiệu suất và đơn giản hóa mã nguồn.

Promise.all()

Promise.all() sẽ chờ tất cả Promise hoàn thành thành công. Nếu chỉ một Promise thất bại thì toàn bộ kết quả sẽ bị từ chối.

Promise.all([

    Promise.resolve("A"),

    Promise.resolve("B"),

    Promise.resolve("C")

])

.then((result) => {
    console.log(result);
});

Kết quả:

["A", "B", "C"]

Phương thức này phù hợp khi tất cả tác vụ đều bắt buộc phải thành công.

Promise.race()

Promise.race() sẽ trả về kết quả của Promise hoàn thành đầu tiên, bất kể thành công hay thất bại.

Promise.race([

    promise1,

    promise2,

    promise3

]);

Phương thức này thường được sử dụng để giới hạn thời gian chờ hoặc ưu tiên phản hồi nhanh nhất.

Promise.allSettled()

Promise.allSettled() sẽ chờ tất cả Promise kết thúc, kể cả thành công lẫn thất bại.

Promise.allSettled([

    promise1,

    promise2,

    promise3

])

.then((result) => {
    console.log(result);
});

Đây là lựa chọn phù hợp khi cần biết kết quả của toàn bộ tác vụ thay vì dừng lại ngay khi có lỗi.

Promise.any()

Promise.any() sẽ trả về Promise đầu tiên thành công. Chỉ khi tất cả Promise đều thất bại thì phương thức này mới phát sinh lỗi.

Promise.any([

    promise1,

    promise2,

    promise3

])

.then((result) => {
    console.log(result);
});

Phương thức này hữu ích khi chỉ cần một nguồn dữ liệu hoạt động là đủ.

Async/Await là gì?

Async/Await là cú pháp được giới thiệu từ chuẩn ECMAScript 2017 (ES8), giúp việc xử lý bất đồng bộ trở nên trực quan và dễ đọc hơn. Về bản chất, Async/Await vẫn được xây dựng trên nền tảng của Promise, nhưng cách viết gần giống với lập trình đồng bộ.

Nhờ đó, mã nguồn trở nên gọn gàng, hạn chế việc lồng nhiều .then() và giúp quá trình bảo trì dễ dàng hơn.

Từ khóa async có tác dụng gì?

Từ khóa async được đặt trước một hàm để biến hàm đó thành hàm bất đồng bộ.

Một hàm async luôn trả về một Promise, dù bạn trả về giá trị thông thường hay Promise.

async function xinChao() {

    return "Xin chào Web Mới";

}

xinChao().then((data) => {
    console.log(data);
});

Kết quả:

Xin chào Web Mới

Mặc dù trả về chuỗi thông thường, JavaScript vẫn tự động bọc kết quả trong một Promise.

Từ khóa await hoạt động như thế nào?

await chỉ được sử dụng bên trong hàm async. Khi gặp await, JavaScript sẽ tạm dừng việc thực thi bên trong hàm hiện tại cho đến khi Promise hoàn thành, sau đó tiếp tục thực hiện các câu lệnh phía dưới.

function layDuLieu() {

    return new Promise((resolve) => {

        setTimeout(() => {
            resolve("Đã lấy dữ liệu");
        }, 2000);

    });

}

async function xuLy() {

    const data = await layDuLieu();

    console.log(data);

}

xuLy();

Sau khoảng 2 giây, kết quả sẽ là:

Đã lấy dữ liệu

Điều cần lưu ý là chỉ riêng hàm chứa await bị tạm dừng. Toàn bộ chương trình JavaScript vẫn tiếp tục hoạt động bình thường.

So sánh Promise và Async/Await

Hai cách viết dưới đây đều cho kết quả giống nhau.

Sử dụng Promise

layDuLieu()

.then((data) => {

    console.log(data);

})

.catch((error) => {

    console.log(error);

});

Sử dụng Async/Await

async function xuLy() {

    try {

        const data = await layDuLieu();

        console.log(data);

    } catch (error) {

        console.log(error);

    }

}

xuLy();

Đối với các chương trình lớn, cách viết bằng Async/Await thường dễ đọc hơn vì luồng thực thi gần giống với cách suy nghĩ tự nhiên của con người.

Xử lý lỗi với try...catch

Khi sử dụng Promise, lỗi thường được xử lý bằng .catch(). Còn với Async/Await, cách phổ biến nhất là sử dụng try...catch.

async function dangNhap() {

    try {

        const data = await layThongTinNguoiDung();

        console.log(data);

    } catch (error) {

        console.log("Có lỗi xảy ra");

    }

}

Cách viết này đặc biệt hữu ích khi phải xử lý nhiều bước liên tiếp vì toàn bộ lỗi đều có thể được bắt tại một vị trí.

Thực hiện nhiều tác vụ bất đồng bộ liên tiếp

Trong nhiều trường hợp, tác vụ phía sau phụ thuộc vào kết quả của tác vụ trước.

async function xuLy() {

    const user = await dangNhap();

    const profile = await layThongTin(user.id);

    const baiViet = await layDanhSachBaiViet(profile.id);

    console.log(baiViet);

}

Đây là một trong những ưu điểm lớn nhất của Async/Await vì mã nguồn rất dễ đọc và gần giống với quá trình xử lý thực tế.

Thực hiện nhiều Promise song song

Nếu các tác vụ không phụ thuộc lẫn nhau thì không nên dùng nhiều await liên tiếp vì sẽ làm chương trình chậm hơn.

Ví dụ chưa tối ưu:

const user = await layNguoiDung();

const sanPham = await laySanPham();

const danhMuc = await layDanhMuc();

Ba Promise trên sẽ chạy lần lượt.

Cách tối ưu hơn là kết hợp với Promise.all().

const [user, sanPham, danhMuc] = await Promise.all([

    layNguoiDung(),

    laySanPham(),

    layDanhMuc()

]);

Lúc này cả ba Promise được thực hiện đồng thời, giúp giảm đáng kể thời gian chờ.

Những lỗi phổ biến khi sử dụng Async/Await

Sử dụng await ngoài hàm async

Đây là lỗi người mới học thường gặp.

const data = await layDuLieu();

Đoạn mã trên sẽ phát sinh lỗi vì await chỉ được phép sử dụng trong hàm async hoặc môi trường hỗ trợ Top-level Await.

Quên xử lý lỗi

Nếu Promise bị từ chối mà không có try...catch, chương trình có thể xuất hiện lỗi không mong muốn.

async function xuLy() {

    try {

        const data = await layDuLieu();

        console.log(data);

    } catch (error) {

        console.log(error);

    }

}

Lạm dụng await liên tiếp

Nhiều lập trình viên mới thường sử dụng await cho mọi Promise mà không để ý các tác vụ có thể chạy song song.

Việc kết hợp với Promise.all() sẽ giúp cải thiện hiệu suất trong rất nhiều trường hợp.

Khi nào nên sử dụng Callback, Promise và Async/Await?

Trường hợp Giải pháp phù hợp
Sự kiện của trình duyệt Callback
API trả về Promise Promise hoặc Async/Await
Nhiều bước xử lý liên tiếp Async/Await
Xử lý nhiều Promise cùng lúc Promise.all()
Cần kết quả đầu tiên Promise.race() hoặc Promise.any()

Bảng so sánh Callback, Promise và Async/Await

Tiêu chí Callback Promise Async/Await
Dễ đọc Thấp Khá Rất cao
Dễ bảo trì Khó Tốt Rất tốt
Xử lý lỗi Khó Dễ Rất dễ
Dễ mở rộng Thấp Cao Rất cao
Khả năng đọc luồng xử lý Khó theo dõi Tốt Tự nhiên nhất

Những kinh nghiệm khi làm việc với JavaScript bất đồng bộ

  • Ưu tiên sử dụng Async/Await trong các dự án hiện đại.
  • Chỉ dùng Callback khi làm việc với các API cũ hoặc các hàm xử lý sự kiện.
  • Luôn xử lý lỗi bằng catch() hoặc try...catch.
  • Không lạm dụng nhiều await liên tiếp nếu các tác vụ có thể chạy đồng thời.
  • Tận dụng Promise.all() để tối ưu hiệu suất.
  • Đặt tên hàm và biến rõ nghĩa để luồng bất đồng bộ dễ theo dõi.
  • Không bỏ qua các Promise bị từ chối vì có thể gây lỗi khó phát hiện.

Event Loop và cơ chế hoạt động của JavaScript bất đồng bộ

Nhiều người mới học thường thắc mắc rằng JavaScript chỉ có một luồng thực thi nhưng vẫn có thể xử lý nhiều tác vụ gần như cùng lúc. Câu trả lời nằm ở Event Loop - cơ chế phối hợp giữa JavaScript Engine và môi trường thực thi như trình duyệt hoặc Node.js.

Điều quan trọng cần hiểu là JavaScript không tự mình thực hiện các tác vụ mất thời gian như gọi API, đợi bộ đếm thời gian hay đọc tệp. Thay vào đó, các công việc này được giao cho môi trường thực thi xử lý. Khi hoàn thành, kết quả sẽ được đưa trở lại để JavaScript tiếp tục thực hiện.

Call Stack

Call Stack là nơi JavaScript thực thi các hàm theo nguyên tắc "vào sau ra trước" (Last In, First Out).

Khi một hàm được gọi, nó sẽ được đưa vào Call Stack. Sau khi thực hiện xong, hàm sẽ được lấy ra khỏi Stack để nhường chỗ cho các hàm tiếp theo.

function a() {
    b();
}

function b() {
    console.log("Web Mới");
}

a();

Quá trình thực hiện sẽ diễn ra như sau:

  1. Hàm a() được đưa vào Call Stack.
  2. a() gọi b().
  3. Hàm b() được thêm vào Stack.
  4. b() thực hiện xong và rời khỏi Stack.
  5. a() kết thúc và rời khỏi Stack.

Web APIs hoặc Node.js APIs

Khi gặp các tác vụ như:

  • setTimeout()
  • setInterval()
  • fetch()
  • XMLHttpRequest
  • Đọc tệp trong Node.js
  • Truy vấn cơ sở dữ liệu

JavaScript sẽ giao các công việc này cho môi trường thực thi thay vì giữ chúng trong Call Stack.

Nhờ đó, Call Stack luôn có thể tiếp tục thực hiện các đoạn mã khác.

Callback Queue

Sau khi một tác vụ bất đồng bộ hoàn thành, hàm Callback của nó sẽ được đưa vào Callback Queue.

Callback chưa được thực hiện ngay lập tức mà phải chờ Call Stack trống.

Event Loop sẽ liên tục kiểm tra:

  • Nếu Call Stack còn đang bận thì tiếp tục chờ.
  • Nếu Call Stack đã trống thì lấy Callback đầu tiên trong hàng đợi để thực hiện.

Microtask Queue

Bên cạnh Callback Queue còn có Microtask Queue. Đây là hàng đợi có mức ưu tiên cao hơn.

Các tác vụ thường nằm trong Microtask Queue gồm:

  • Promise.then()
  • Promise.catch()
  • Promise.finally()
  • queueMicrotask()
  • MutationObserver

Event Loop luôn xử lý toàn bộ Microtask Queue trước khi chuyển sang Callback Queue.

Ví dụ minh họa Event Loop

console.log("1");

setTimeout(() => {
    console.log("2");
}, 0);

Promise.resolve().then(() => {
    console.log("3");
});

console.log("4");

Kết quả:

1
4
3
2

Giải thích:

  1. Hai lệnh console.log() đầu tiên được thực hiện ngay.
  2. Promise được đưa vào Microtask Queue.
  3. setTimeout() được đưa vào Callback Queue.
  4. Call Stack trống.
  5. Event Loop ưu tiên thực hiện Microtask Queue.
  6. Sau đó mới đến Callback Queue.

Đây là lý do Promise thường được thực hiện trước setTimeout(), kể cả khi thời gian chờ là 0 mili giây.

Best Practices khi lập trình bất đồng bộ

Ưu tiên Async/Await cho mã nguồn mới

Đối với các dự án hiện đại, Async/Await giúp mã nguồn ngắn gọn, rõ ràng và dễ bảo trì hơn Promise thuần.

Không bỏ qua việc xử lý lỗi

Mỗi Promise đều có khả năng thất bại. Vì vậy, luôn sử dụng .catch() hoặc try...catch để tránh lỗi không được kiểm soát.

Không lồng quá nhiều Promise

Việc lồng nhiều Promise bên trong nhau sẽ làm mã nguồn khó đọc. Hãy trả về Promise và tiếp tục xử lý bằng chuỗi .then() hoặc Async/Await.

Chạy song song khi có thể

Nếu các tác vụ không phụ thuộc vào nhau, hãy để chúng thực hiện đồng thời thay vì chờ từng tác vụ hoàn thành.

Điều này giúp giảm đáng kể thời gian xử lý của ứng dụng.

Đặt tên hàm theo đúng chức năng

Những hàm bất đồng bộ nên có tên thể hiện rõ mục đích như:

  • layDanhSachSanPham()
  • taiThongTinNguoiDung()
  • guiYeuCauThanhToan()
  • kiemTraDangNhap()

Việc đặt tên rõ ràng sẽ giúp luồng bất đồng bộ dễ theo dõi hơn khi dự án phát triển lớn.

Những hiểu lầm phổ biến về JavaScript bất đồng bộ

Async/Await làm chương trình chạy nhanh hơn

Đây là quan niệm chưa chính xác. Async/Await không giúp tăng tốc độ thực thi của Promise mà chỉ thay đổi cách viết mã nguồn để dễ đọc và dễ bảo trì hơn.

await sẽ chặn toàn bộ JavaScript

await chỉ tạm dừng bên trong hàm async hiện tại. Những đoạn mã khác vẫn tiếp tục được thực hiện bình thường.

setTimeout(..., 0) sẽ chạy ngay lập tức

Thời gian 0 mili giây chỉ là khoảng thời gian tối thiểu trước khi Callback được đưa vào hàng đợi. Hàm vẫn phải chờ Call Stack trống và Event Loop xử lý.

Promise luôn thay thế Callback

Callback vẫn rất cần thiết trong nhiều trường hợp như xử lý sự kiện của trình duyệt, lắng nghe thao tác người dùng hoặc làm việc với một số thư viện cũ.

Kết luận

Lập trình bất đồng bộ là nền tảng quan trọng giúp JavaScript xử lý hiệu quả các tác vụ như gọi API, tải dữ liệu, đọc tệp và tương tác với người dùng mà không làm treo ứng dụng.

Callback là phương pháp đầu tiên để giải quyết các tác vụ bất đồng bộ nhưng dễ dẫn đến Callback Hell khi chương trình trở nên phức tạp. Promise ra đời giúp tổ chức luồng xử lý rõ ràng hơn, đồng thời đơn giản hóa việc xử lý lỗi. Trên nền tảng đó, Async/Await mang đến cú pháp trực quan, gần với lập trình đồng bộ và hiện là lựa chọn được ưu tiên trong hầu hết các dự án JavaScript hiện đại.

Để viết mã nguồn hiệu quả, bạn không chỉ cần nắm vững Callback, Promise và Async/Await mà còn nên hiểu cách Event Loop, Call Stack, Callback Queue và Microtask Queue phối hợp với nhau. Khi hiểu rõ cơ chế này, bạn sẽ dễ dàng xây dựng các ứng dụng web có hiệu năng tốt, dễ bảo trì và xử lý các tình huống bất đồng bộ một cách chính xác.

  • 0 Bình luận
Trần Thị Vân | Content Creator Web Mới
Trần Thị Vân
Trần Thị Vân là Content Creator tại Web Mới, phụ trách nghiên cứu, biên soạn nội dung và chia sẻ kiến thức về website, SEO, lập trình cùng các xu hướng công nghệ
Chia sẻ nội dung đánh giá của bạn về JavaScript bất đồng bộ: Callback, Promise và Async/Await
Email, Điện thoại 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 *
Đánh giá của bạn
Tên *
Email
Số điện thoại *
Bình luận, Hỏi đáp
Đăng ký tư vấn thiết kế website
Tìm hiểu 1 năm không bằng lắng nghe 1 câu tư vấn