Callback function in Javascript

Trong bài viết này, chúng ta sẽ cùng tìm hiểu về callback trong JS, nó là dễ học, dễ hiểu. Nhưng khi đi phỏng vấn mình thấy nhiều bạn vẫn còn lơ mơ về khái niệm này, nên trong phần này mình sẽ chia sẻ một cách đầy đủ nhất về khái niệm callback này nhé.
Alt Text

1. Mở đầu

Trong JS, function còn được gọi là first-class objects, điều này có nghĩa là function cũng giống như các object bình thường khác. Ta có thể gắn object vào 1 argument trong function thì cũng có thể gắn 1 function vào argument của 1 hàm, hàm này được gọi ở 1 thời điểm nào đó bên trong hàm mà nó gắn và như vậy hàm được truyền vào qua argument của 1 hàm được gọi là callback function.

Ở đây chúng ta phải phân biệt rõ ràng được trong trường hợp 1 hàm được gọi bên trong 1 hàm thì hàm này không được gọi là hàm callback mà nó phải truyền thông qua argument của function mà nó được gọi.

2. Chi tiết.

Như khái niệm mình đã cắt nghĩa ở bên trên, thì đây chính là khái niệm trên w3shool:

"I will call back later!"

A callback is a function passed as an argument to another function

This technique allows a function to call another function

A callback function can run after another function has finished

Một ví dụ cơ bản nhất mà mình tin chắc rằng bạn sẽ hình dung ra callback trong Js là như thế nào:

// A function
function fn() {
  console.log('Just a function');
}

// A function that takes another function as an argument
function higherOrderFunction(callback) {
  // When you call a function that is passed as an argument, it is referred to as a callback
  callback();
}

// Passing a function
higherOrderFunction(fn);

Dưới đây sẽ là một số ví dụ mà bạn có thể làm với callback function:

Dùng để hanlde khi gắn event:

function callback(e) {
    alert(e.target.innerText);
}

document.getElementById("name").addEventListener("click", callback);

Mình nghĩ là phần lớn các bạn đọc bài viết này sẽ không gắn sự kiện vào thẻ kiểu như thế này nữa. Nhưng có lẽ đây là phần cơ bản nhất cũng như khá là dễ hiểu về một ứng dụng của callback mà có lẽ các bạn đã dùng rồi mà lại không ý thức được là mình đã dùng nó. Ở đây hàm callback sẽ được gọi lại mỗi khi chúng ta click vào thẻ có id là name.

Dùng để hanlde xử lí 1 tác vụ bất đồng bộ:

JS là ngôn ngữ chạy đồng bộ và single thread, nghĩa là nó sẽ chạy từ trên xuống dưới (Sau khi hoisting). Như vậy nếu trong trường hợp có một tiến trình nào đó bất đồng bộ hoặc hiểu theo cách khác là chúng ta đang handle một tiến trình nào đó khá nặng mà lại cần những kết quả của nó để có thể làm tiếp việc khác thì sau khi handle xong chúng ta vẫn cần callback function.

Dưới đây là ví dụ mà bạn có thể tham khảo thêm nhé:

function handleLog(posts) {
    console.log(JSON.stringify(posts));
}

const fetchData = (handleLog) => {
    fetch("http://localhost:3000/posts")
        .then(res => res.json())
        .then(posts => handleLog(posts));
};

fetchData(handleLog);

Như vậy bạn có thể thấy được là handleLog function cũng chính là một callback function, nó sẽ được gọi sau khi mà bạn đã call api và nó đã reponse về. (Ở đây mình đang dùng json-server để fake api và cho nó delay 5s).

This trong callback function:

Đây là một phần khá nhiều trường hợp ngoại lệ, nên có lẽ mình sẽ nói tóm gọn nhất có thể nhé.

- Đây có lẽ là đoạn code đơn giản nhất, nhưng KHOAN hãy Đoán Xem đoạn code này sẽ chạy ra gì nào ^^:

function callback() {
    console.log("this => callback function", this);
};

function handleThis(callback) {
    this.address = "thaibinh";
    console.log("this => internal function", this);
    callback();
};

handleThis(callback);

Okay, nó sẽ log ra this của 2 thằng kia đều là window. Và tiếp tục đoán tiếp xem đoạn này nó chạy ra gì nào ^^:

function callback() {
    console.log("this => callback function", this);
};

function handleThis(callback) {
    const obj = {
        name: "chamdev.com",
        callbackObj = callback(),
    };
    console.log("this => internal function", this);
    obj.callbackObj();
};

handleThis(callback);

Có thể nhiều bạn đã đoán đúng rồi đúng không nào, internal sẽ là window còn this callback ở đây lại là obj Object.

Tiếp tục nào (Keep going....). Đoạn call này nó sẽ log ra cái gì nào???

function callback() {
    console.log("this => callback function", this);
};

function handleThis(callback) {
    new callback();
};

handleThis(callback);

Mình cá là nhiều bạn đoán sai, nếu bán chạy thử kết quả sẽ là chính cái callback function đó nhé. Như vậy ở phần này mình muốn trình bày với các bạn một số cách để có thể gắn lại context vào callback function, trong một số trường hợp oái oăm nào đó mà bạn cần dùng đến context của nó.

Callback HELL và cách khắc phục:

Ví dụ callback hell:

const makeBurger = nextStep => {
  getBeef(function (beef) {
    cookBeef(beef, function (cookedBeef) {
      getBuns(function (buns) {
        putBeefBetweenBuns(buns, beef, function(burger) {
          nextStep(burger);
        });
      });
    });
  });
};

Đầu tiên chúng ta cần hiểu HELL ở đây là như thế nào?
=> HELL nghĩa là rối rắm, khó debbug, khó maintain về sau có check lại đoạn code cũng sẽ thấy rất ngại.

Để giải quyết vấn đề Callback hell như trên chúng ta có thể comment code, đặt lại tên hàm cho tách biệt ra - viết sao cho nó trở nên ngắn gọn. Hoặc bạn có thể chuyển hẳn sang Promise hoặc asyn-await. Nhưng suy cho cùng HELL hay không là tại bạn thôi ;)))) - Solutions mình nêu ở trên chỉ là cho sự hell trở nên đỡ hơn.

3, Tham khảo.

4. Kết luận.

Hiểu callback là truyền 1 hàm vào tham số của 1 hàm khác, ở 1 thời điểm nào đó thì hàm callback sẽ được gọi trong hàm này.

Callback phải là 1 function.

This trong callback mặc định chính là window, nếu muốn dùng context của đối tượng nào đó thì phải gắn lại.

29