Lập trình bất đồng bộ (asynchronous programming) trong JavaScript là một phương pháp lập trình cho phép một tác vụ bắt đầu mà không cần chờ hoàn thành để tiếp tục thực hiện các tác vụ khác. Trong lập trình đồng bộ, các lệnh được thực thi tuần tự; ngược lại, trong lập trình bất đồng bộ, các tác vụ có thể được thực thi song song hoặc đồng thời, giúp tăng hiệu quả và tránh việc làm chậm quá trình xử lý của chương trình.

JavaScript là ngôn ngữ đơn luồng, tức là chỉ có một luồng thực thi chính. Khi một tác vụ bất đồng bộ được thực hiện, JavaScript sử dụng cơ chế Event Loop để xử lý các tác vụ này, cho phép nó tiếp tục thực thi mã khác trong khi chờ các tác vụ dài hạn như truy vấn mạng, truy xuất file, hoặc bộ đếm thời gian hoàn tất.

Các kỹ thuật lập trình bất đồng bộ trong JavaScript:

  1. Callbacks
  2. Promises
  3. Async/Await

1. Callbacks

Callback là một hàm được truyền như một tham số vào một hàm khác và được gọi sau khi tác vụ đã hoàn thành.

Ví dụ với Callback:

function doSomethingAsync(callback) {
    setTimeout(() => {
        console.log('Task done');
        callback();
    }, 2000);
}

doSomethingAsync(() => {
    console.log('Callback executed');
});

Trong ví dụ trên, sau 2 giây, chương trình thực hiện console.log('Task done') và gọi hàm callback để tiếp tục.

Vấn đề với Callback: Callback có thể dẫn đến cái gọi là “Callback Hell”, nơi các callback lồng nhau dẫn đến mã trở nên khó đọc và bảo trì.

2. Promises

Promise cung cấp một cách tiếp cận sạch sẽ hơn để xử lý các tác vụ bất đồng bộ. Promise đại diện cho một giá trị chưa biết tại thời điểm nó được tạo ra nhưng sẽ được giải quyết (resolve) hoặc từ chối (reject) trong tương lai.

Ví dụ với Promise:

const doSomethingAsync = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('Task done');
        }, 2000);
    });
};

doSomethingAsync()
    .then((message) => {
        console.log(message);  // 'Task done'
    })
    .catch((error) => {
        console.error(error);
    });

Promise giúp tránh lồng callback và cho phép chuỗi các tác vụ bất đồng bộ được thực hiện theo cách dễ hiểu và có thể quản lý được.

3. Async/Await

asyncawait là cú pháp được giới thiệu trong ES2017, cung cấp cách thức viết code bất đồng bộ trông giống như mã đồng bộ, giúp nó dễ đọc hơn và tránh được các vấn đề về callback hell hoặc quản lý Promise.

Ví dụ với Async/Await:

const doSomethingAsync = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('Task done');
        }, 2000);
    });
};

async function doTask() {
    try {
        const result = await doSomethingAsync();
        console.log(result);  // 'Task done'
    } catch (error) {
        console.error(error);
    }
}

doTask();

Trong ví dụ trên, từ khóa await đợi cho Promise được giải quyết trước khi tiếp tục với mã tiếp theo, giúp mã trông rõ ràng và dễ hiểu hơn mà không cần xử lý .then().

Lợi ích của lập trình bất đồng bộ:

  1. Hiệu suất cao hơn: JavaScript có thể xử lý nhiều tác vụ đồng thời mà không bị tắc nghẽn, đặc biệt là các tác vụ tốn thời gian như truy vấn API, xử lý file.
  2. Trải nghiệm người dùng tốt hơn: Trong ứng dụng web, lập trình bất đồng bộ đảm bảo rằng người dùng có thể tiếp tục tương tác với giao diện trong khi các tác vụ nền được xử lý.
  3. Quản lý tài nguyên tốt hơn: Việc sử dụng tài nguyên của máy tính trở nên tối ưu hơn khi các tác vụ có thể được thực hiện song song mà không cần phải chờ đợi tác vụ trước hoàn thành.

Kết luận:

Lập trình bất đồng bộ trong JavaScript rất quan trọng trong việc xử lý các tác vụ dài hạn mà không làm tắc nghẽn luồng chính. Các công cụ như callback, promise, và async/await cung cấp những cách khác nhau để xử lý bất đồng bộ, với async/await là phương pháp hiện đại, dễ đọc và bảo trì nhất.