1. Hàm callback là gì?

Trong lập trình máy tính, callback là một tham chiếu đến đoạn mã thực thi (hoặc chính đoạn mã thực thi đó) được truyền như một đối số cho đoạn mã khác. Nhờ vậy, một tầng phần mềm cấp thấp có thể gọi một chương trình con (hoặc hàm) được định nghĩa ở tầng cấp cao hơn.

Nguồn: Wikipedia

2. Cách cài đặt hàm callback trong Java

Nguồn: xyzws.com

Hàm callback là đoạn mã thực thi được gọi thông qua một con trỏ hàm. Bạn truyền con trỏ hàm callback như một đối số cho đoạn mã khác, hoặc đăng ký nó ở đâu đó. Khi có sự kiện xảy ra, hàm callback sẽ được gọi.

C/C++ cho phép dùng con trỏ hàm làm đối số cho các hàm khác, nhưng Java không có con trỏ. Trong Java, chỉ có đối tượng và kiểu dữ liệu nguyên thủy mới được truyền vào các phương thức của lớp. Tuy nhiên, cơ chế interface của Java cho phép ta đạt được hiệu quả tương đương callback: bạn khai báo một interface chứa hàm mà bạn muốn truyền đi.

Lưu ý: bài gốc được viết khi Java chưa có lambda và method reference (từ Java 8 trở đi). Hiện nay bạn có thể truyền callback gọn hơn nhiều bằng biểu thức lambda, nhưng cơ chế interface bên dưới vẫn như vậy.

Collections.sort(List list, Comparator c) là một ví dụ về cài đặt callback trong Java. Tham số c là một thể hiện của lớp cài đặt phương thức compare(e1, e2) trong interface Comparator. Phương thức này sắp xếp danh sách theo thứ tự do comparator quy định. Mọi phần tử trong danh sách phải so sánh được với nhau bằng comparator đó.

Bạn có thể dùng inner class (lớp bên trong) để định nghĩa một lớp callback ẩn danh, khởi tạo đối tượng callback ẩn danh và truyền nó làm tham số, tất cả chỉ trong một dòng. Một cách dùng phổ biến của inner class là cài đặt các interface trong xử lý sự kiện.

class SomePanel extends JPanel {

    private JButton    myGreetingButton = new JButton("Hello");
    private JTextField myGreetingField  = new JTextField(20);

    private ActionListener doGreeting = new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            myGreetingField.setText("Hello");
        }
    };

    public SomePanel() {
        myGreetingButton.addActionListener(doGreeting);
        // . . . Bố trí giao diện cho panel.
    }

}

3. Dùng interface để cài đặt tương đương hàm callback trong Java

Nguồn: JavaWorld – Tác giả: John D. Mitchell, JavaWorld.com, ngày 1 tháng 6 năm 1996.

Các lập trình viên quen với mô hình lập trình hướng sự kiện của MS-Windows và X Window System thường truyền con trỏ hàm để chúng được gọi (tức là "gọi lại" – called back) khi có việc xảy ra. Mô hình hướng đối tượng của Java hiện không hỗ trợ con trỏ phương thức, nên có vẻ như không thể dùng cơ chế quen thuộc này. Nhưng không phải là hết cách!

Cơ chế interface của Java cho ta một cách để đạt được hiệu quả tương đương callback. Mẹo ở đây là định nghĩa một interface đơn giản khai báo phương thức mà ta muốn được gọi.

Ví dụ, giả sử ta muốn được thông báo khi một sự kiện xảy ra. Ta có thể định nghĩa interface:

public interface InterestingEvent {
    // Đây chỉ là một phương thức bình thường nên có thể trả về giá trị
    // hoặc nhận tham số nếu bạn muốn.
    public void interestingEvent();
}

Cách này giúp ta nắm được mọi đối tượng của các lớp cài đặt interface, nên không cần bận tâm đến bất kỳ thông tin kiểu nào khác. Cách này dễ chịu hơn nhiều so với việc tự chế các hàm "trampoline" bằng C, dùng trường data của widget để giữ con trỏ đối tượng khi dùng mã C++ với Motif.

Lớp phát tín hiệu sự kiện cần nhận các đối tượng cài đặt interface InterestingEvent, rồi gọi phương thức interestingEvent() khi cần.

public class EventNotifier {
    private InterestingEvent ie;
    private boolean somethingHappened;

    public EventNotifier(InterestingEvent event) {
        // Lưu đối tượng sự kiện để dùng sau.
        ie = event;
        // Hiện chưa có gì để báo.
        somethingHappened = false;
    }

    //...

    public void doWork() {
        // Kiểm tra điều kiện (được gán ở nơi khác).
        if (somethingHappened) {
            // Báo hiệu sự kiện bằng cách gọi phương thức của interface.
            ie.interestingEvent();
        }
    }
}

Trong ví dụ trên, mình dùng cờ điều kiện somethingHappened để theo dõi xem có nên kích hoạt sự kiện hay không. Trong nhiều trường hợp, chỉ cần phương thức được gọi là đã đủ để phát tín hiệu interestingEvent().

Đoạn mã muốn nhận thông báo sự kiện phải cài đặt interface InterestingEvent và chỉ cần truyền tham chiếu của chính nó cho đối tượng thông báo sự kiện.

public class CallMe implements InterestingEvent {
    private EventNotifier en;

    public CallMe() {
        // Tạo đối tượng thông báo sự kiện và truyền chính mình vào.
        en = new EventNotifier(this);
    }

    // Định nghĩa bộ xử lý thực sự cho sự kiện.
    public void interestingEvent() {
        // Chà! Chắc hẳn đã có chuyện gì đó rất thú vị xảy ra!
        // Hãy làm gì đó...
    }

    //...
}

Chỉ có vậy thôi. Hy vọng thủ thuật Java đơn giản này sẽ giúp bạn chuyển sang Java bớt bỡ ngỡ hơn.

Về tác giả

Sống nhờ caffeine, đường và rất ít giấc ngủ, John D. Mitchell đã làm tư vấn trong phần lớn chín năm qua, và từng phát triển phần mềm PDA bằng hợp ngữ hướng đối tượng tại Geoworks. Anh nuôi "cơn nghiện Java" của mình bằng việc viết trình biên dịch và các hệ thống Tcl/Tk, C++ và Java. Anh là đồng tác giả cuốn sách Java mới ra mắt và rất được chú ý Making Sense of Java, và hiện đang phát triển một trình biên dịch Java.

4. Viết các hàm callback

Nguồn: CodeGuru (Thinking in Java)

Một số hàm của Win32 API yêu cầu một con trỏ hàm làm tham số. Hàm Windows API có thể gọi hàm được truyền vào, có thể vào một thời điểm sau đó khi có sự kiện xảy ra. Kỹ thuật này gọi là hàm callback. Ví dụ gồm các window procedure và các callback bạn thiết lập trong quá trình in (bạn đưa cho print spooler địa chỉ hàm callback để nó cập nhật trạng thái và có thể ngắt việc in).

Một ví dụ khác là hàm API EnumWindows(), dùng để liệt kê tất cả cửa sổ cấp cao nhất hiện có trong hệ thống. EnumWindows() nhận một con trỏ hàm, rồi duyệt qua danh sách do Windows quản lý nội bộ. Với mỗi cửa sổ trong danh sách, nó gọi hàm callback và truyền handle của cửa sổ làm đối số.

5. Hàm callback trong jQuery

Nguồn: jQuery Docs

Callback là một hàm được truyền làm đối số cho một hàm khác và được thực thi sau khi hàm "cha" hoàn tất. Điểm đặc biệt của callback là các hàm nằm sau hàm "cha" có thể chạy trước khi callback được thực thi. Một điều quan trọng khác là phải biết cách truyền callback đúng cú pháp, chỗ mà mình đã nhiều lần quên.

Callback không có đối số

Với callback không có đối số, bạn truyền như sau:

$.get('myhtmlpage.html', myCallBack);

Sau khi nhận xong phản hồi GET, myCallBack sẽ được thực thi.

6. jQuery 1.3 với PHP

Tuy nhiên, trong JavaScript, bạn có thể gọi một số hàm và để chúng trả kết quả về cho hàm "callback" sau vài giây.

7. Tích hợp chức năng Ajax vào một component JavaServer Faces tùy chỉnh

Hàm callback là hàm được đối tượng XMLHttpRequest gọi, tương tự như việc nó được gọi từ server (thông thường thì client mới là bên gọi các hàm trên server). Nhưng vì đối tượng XMLHttpRequest thuộc về trình duyệt, nên hàm callback là hàm được trình duyệt gọi sau khi hàm chính hoàn thành, để thực hiện thêm một số tác vụ phụ.

8. Google Web Toolkit

// todo

Dựa trên: