Callbacks trong JavaScript

Callbacks trong JavaScript

Tóm tắt: trong hướng dẫn này, bạn sẽ tìm hiểu về các hàm gọi lại trong JavaScript bao gồm các hàm gọi lại đồng bộ và không đồng bộ.

Các bài viết liên quan:

Callbacks function là gì ?

Trong JavaScript, một cuộc gọi lại là một hàm được truyền vào một hàm khác như một đối số sẽ được thực thi sau này.

Giả sử rằng bạn dãy số sau:

let numbers = [1, 2, 4, 7, 3, 5, 6];

Để tìm tất cả các số lẻ trong mảng, bạn có thể sử dụng phương thức filter () của đối tượng Array.

Phương thức filter () tạo một mảng mới với các phần tử vượt qua thử nghiệm được thực hiện bởi một hàm.

Hàm kiểm tra sau trả về true nếu một số là số lẻ:

function isOddNumber(number) {
    return number % 2; // trả về 0 nếu chẵn, 1 nếu lẻ
}

Bây giờ, bạn có thể chuyển isOddNumber () vào phương thức filter ():

const oddNumbers = numbers.filter(isOddNumber);
console.log(oddNumbers); // [ 1, 7, 3, 5 ]

Trong ví dụ này, isOddNumber là một hàm gọi lại. Khi bạn truyền một hàm gọi lại vào một hàm khác, bạn chỉ cần truyền tham chiếu của hàm, tức là tên hàm mà không có dấu ngoặc đơn ().

Để làm cho nó ngắn hơn, bạn có thể sử dụng một hàm ẩn danh như một hàm callback:

let oddNumbers = numbers.filter(function(number) {
    return number % 2;
});
console.log(oddNumbers); // [ 1, 7, 3, 5 ]

Trong ES6, bạn có thể sử dụng các hàm mũi tên:

let oddNumbers = numbers.filter(number => number % 2); // rút gọn trên ES6

Khi bạn sử dụng JavaScript trên các trình duyệt web, bạn thường nghe một sự kiện, ví dụ: một lần nhấp vào nút và thực hiện một số hành động nếu sự kiện xảy ra.

Giả sử rằng bạn có một nút với id btn:

<button id="btn">Save</button>

Để thực thi một số mã khi nút được nhấp, bạn sử dụng một lệnh gọi lại và chuyển nó vào phương thức addEventListener ():

function btnActionClicked() { 
   // do something here
}
let btn = document.querySelector('#btn'); // lấy  id button
btn.addEventListener('click',btnActionClicked); // thêm event addEventListener

BtnClicked trong ví dụ này là một function call. Khi nút được nhấp, hàm btnClicked () được gọi để thực hiện một số hành động.

Bây giờ, bạn đã có những ý tưởng cơ bản về callback: truyền một hàm vào một hàm khác.

Các hàm Callback được sử dụng theo hai cách: hàm đồng bộ và hàm không đồng bộ.

Các hàm callback Synchronous(đồng bộ)

Nếu code của bạn thực thi tuần tự từ trên xuống dưới, nó là đồng bộ. Hàm isOddNumber () là một ví dụ về hàm gọi lại đồng bộ.

Trong ví dụ sau, hàm mũi tên là một lệnh gọi lại được sử dụng trong một hàm đồng bộ.

Phương thức sort () hoàn thành đầu tiên trước khi console.log () thực thi:

let numbers = [1, 2, 4, 7, 3, 5, 6];
numbers.sort((a, b) => a - b);
console.log(numbers); // [ 1, 2, 3, 4, 5, 6, 7 ]

Các hàm Asynchronous callback

Không đồng bộ có nghĩa là nếu JavaScript phải đợi một hoạt động hoàn thành, nó sẽ thực thi phần còn lại của mã trong khi chờ đợi.

Lưu ý rằng JavaScript là một ngôn ngữ lập trình đơn luồng. Nó thực hiện các hoạt động không đồng bộ thông qua hàng đợi gọi lại và vòng lặp sự kiện.

Giả sử rằng bạn cần phát triển một tập lệnh tải xuống ảnh từ máy chủ từ xa và xử lý sau khi quá trình tải xuống hoàn tất:

function download(url) {
    // ...
}

function process(picture) {
    // ...
}

download(url);
process(picture);

Tuy nhiên, việc tải ảnh từ máy chủ từ xa sẽ mất thời gian tùy thuộc vào tốc độ mạng và kích thước của ảnh.

Đoạn mã sau sử dụng hàm setTimeout () để mô phỏng hàm download ():

function download(url) {
    setTimeout(() => {
        // script tiến hành download  từ url
        console.log(`Downloading ${url} ...`);
    }, 3* 1000);
}

Và đoạn mã này mô phỏng hàm process ():

function process(picture) {
    console.log(`Processing ${picture}`);// download picture
}

Khi bạn thực thi mã sau:

let url = 'https://www.website.net/zoo.jg';

download(url);
process(url);

bạn sẽ nhận được kết quả sau:

Processing https:
Downloading https:

Đây không phải là điều bạn mong đợi vì hàm process () thực thi trước hàm download (). Trình tự đúng phải là:

  • Tải xuống hình ảnh, đợi cho đến khi hoàn thành.
  • Xử lý hình ảnh.

Để khắc phục sự cố ở trên, bạn có thể chuyển hàm process () cho hàm download () và thực thi hàm process () bên trong hàm download () sau khi quá trình tải xuống hoàn tất, như sau:

function download(url, callback) {
    setTimeout(() => {
        // download url image
        console.log(`Downloading ${url} ...`);
        
        // tiến hành download
        callback(url);
    }, 3000);
}

function process(picture) {
    console.log(`Processing ${picture}`); // download
}

let url = 'https://wwww.websitehcm.com/picture.jpg';
download(url, process);

Bây giờ, nó hoạt động như mong đợi.

Trong ví dụ này, process () là một lệnh gọi lại được truyền vào một hàm không đồng bộ.

Khi bạn sử dụng lệnh callback để tiếp tục thực thi mã sau các hoạt động không đồng bộ, các lệnh gọi lại này được gọi là lệnh gọi lại không đồng bộ.

Bằng cách sử dụng lệnh gọi lại không đồng bộ, bạn có thể đăng ký trước một hành động mà không chặn toàn bộ hoạt động.

Để làm cho mã sạch hơn, bạn có thể xác định hàm process () như một hàm ẩn danh:

Xử lý lỗi

Hàm download () giả định rằng mọi thứ hoạt động tốt và không xem xét bất kỳ trường hợp lỗi nào. Đoạn mã sau giới thiệu hai lệnh callback: thành công và thất bại để xử lý các trường hợp thành công và thất bại tương ứng:

function download(url, success, failure) {
    setTimeout(() => {
        // đơnload url
        console.log(`Downloading ${url} ...`);
        // nếu lỗi 
        let error = url.length === 0 || !url; 
        // gọi hàm xử lý lỗi
        error ? failure(url) :  success(url);
    }, 3000);
}

download('',
    function(picture) {
        console.log(`Processing the picture ${picture}`);// đowload
    },
    function(picture) {
        console.log(`Handling error...`); // xử lý lỗi
    }
);

Callback và Pyramid of Doom

Làm thế nào để bạn tải xuống ba bức ảnh và xử lý chúng một cách tuần tự? Một cách tiếp cận điển hình là gọi hàm download () bên trong hàm gọi lại, như sau:

function download(url, callback) {
    setTimeout(() => {
        // tiến hành download url ở đây
        console.log(`Downloading ${url} ...`);
        // trong quá trình download 
        callback(url);
    }, 3000);
}

const url1 = 'https://www.websitehcm.net/pic1.jpg';
const url2 = 'https://www.websitehcm.net/pic2.jpg';
const url3 = 'https://www.websitehcm.net/pic3.jpg';

download(url1,function(picture){
    console.log(`Processing ${picture}`);
    // đơnload bức hình 2
    download(url2,function(picture){
        console.log(`Processing ${picture}`);
        // download bức hình 3
        download(url3,function(picture){
            console.log(`Processing ${picture}`);
        });
    });
});

Kịch bản hoạt động hoàn toàn tốt.

Tuy nhiên, chiến lược gọi lại này không mở rộng quy mô tốt khi độ phức tạp tăng lên đáng kể.

Việc lồng nhiều hàm không đồng bộ bên trong các lệnh gọi lại được gọi là Pyramid of Doom:

asyncFunction(function(){
    asyncFunction(function(){
        asyncFunction(function(){
            asyncFunction(function(){
                asyncFunction(function(){
                    ....
                });
            });
        });
    });
});

Để tránh Pyramid of Doom, bạn sử dụng các hàm Promise hoặc async / await.

Tóm lược

Gọi lại là một hàm được truyền vào một hàm khác như một đối số sẽ được thực thi sau này.

Các hàm gọi lại có thể đồng bộ hoặc không đồng bộ.

Quý khách có thể tham khảo hơn ở các dịch vụ do websitehcm.com cung cấp như: dịch vụ seo, dịch vụ viết content , dịch vụ chăm sóc website, dịch vụ thiết kế website 

Leave a Reply