11/08/2018, 19:59

JavaScript - Debounce Function là gì?

Trong một bài test của mình, có một câu là hãy thực hiện Instant Search giống của Twitter. Cách sử dụng là khi bạn nhập một ký tự thì sẽ gởi một request lên Server để lấy dữ liệu. Vấn đề đặt ra var txtKeyword = $('#key-word'), lstResult = $('#result'), keyword = ...

Trong một bài test của mình, có một câu là hãy thực hiện Instant Search giống của Twitter. Cách sử dụng là khi bạn nhập một ký tự thì sẽ gởi một request lên Server để lấy dữ liệu.

alt text

Vấn đề đặt ra

var txtKeyword = $('#key-word'),
    lstResult = $('#result'),
    keyword = txtKeyword.val();

$.ajax(function() {
  method: 'GET',
  url: 'http://example.com/getdata',
  data: {
    keyword: keyword
  }
}).done(function(data) {
  var people = JSON.parse(data),
      html = ';

  people.forEach(function(person) {
    html += '<li>' + person.name + '</li>';
  });

  lstResult.empty();
  lstResult.append(html);
});

Bạn viết đoạn code như trên. Khi bạn nhập "abc" thì sẽ có 3 request được gởi đi lần lượt cho "a", "ab""abc". Giả sử, thứ tự dữ liệu được trả về lần lượt là "a", "abc""ab". Có gì đó sai sai ở đây?

alt text

Trong khi đó, kết quả bạn cần là "abc" chứ không phải là "ab". Như vậy, lỗi này sẽ ảnh hưởng tới UX (User Experience). Để giải quyết vấn đề đó sẽ có rất nhiều cách. Mình tin là có bác sẽ nghĩ tới chuyện chờ người dùng nhập xong khoảng độ 1 giây, rồi lấy kết quả cuối cùng đem đi gởi. Vậy làm sao để biết nó khi nào nhập xong ta? Lúc đó thì ta sẽ sử dụng một kỹ thuật có tên là Debounce.

Debounce Function

Đầu tiên, bạn sẽ muốn gọi hàm debounce(func, wait), truyền vào hàm cần thực thi và thời gian delay. Do đó, bạn sẽ viết hàm debounce sẽ trả về một hàm anonymous.

function debounce(func, wait) {
  return function() {
    // TODO
  }
}

Sau đó, bạn sẽ cần lấy giá trị của các tham số truyền vào.

function debounce(func, wait) {
  return function() {
    var context = this,
        args = arguments;
  }
}

Tiếp theo là bạn tạo một hàm tên là executeFunction để khi gọi hàm này, thì hàm được truyền vào sẽ thực thi.

function debounce(func, wait) {
  return function() {
    var context = this,
        args = arguments;

    var executeFunction = function() {
      func.apply(context, args);
    }
  }
}

Bây giờ, ta gọi hàm setTimeout để delay việc thực thi mỗi khi gọi hàmdebounce tương ứng với wait giây.

function debounce(func, wait) {
  return function() {
    var context = this,
        args = arguments;

    var executeFunction = function() {
      func.apply(context, args);
    }

    setTimeout(executeFunction, wait);
  }
}

Quay lại với giải pháp đã đưa ra, đoạn code tới đây đã giải quyết được vấn đề mỗi khi người dùng nhập thì delay một khoảng thời gian mới thực thi hàm. Nhưng có gì đó sai sai! Hình như là nó chưa lấy được giá trị cuối cùng của người dùng sau khi nhập rồi mới gởi request. Ta chỉ cần xóa đi các lần gọi hàm trước đó là được chứ gì, dùng hàm clearTimeout.

function debounce(func, wait) {
  var timeout;

  return function() {
    var context = this,
        args = arguments;

    var executeFunction = function() {
      func.apply(context, args);
    };

    clearTimeout(timeout);
    timeout = setTimeout(executeFunction, wait);
  };
};

Như vậy là đã viết xong hàm debounce. Để sử dụng thì ta viết như sau.

// Usage
var handleClick = debounce(function (e) {
  // do stuff here
  console.log(e);
}, 500);

$('button').on('click', handleClick);

Chuyện bên lề

Trên blog của YinYangIT có đề cập tới một chiêu thức khác đó là requestAnimationFrame() thay thế cho setTimeout(). Bạn thậm chí có thể không cần đến tham số wait khi sử dụng phương pháp này. Nếu bạn nào có thời gian nghiên cứu thì tìm hiểu thêm.

Hàm debounce đã được một số thư viện hỗ trợ như là Underscore.js, Lodash. Thực ra, đoạn code được dùng demo là của Underscore phiên bản cũ. Cá nhân thì thấy nếu bạn có nhu cầu tìm hiểu các kỹ thuật khi làm việc với JavaScript, nên đọc mã nguồn của Underscore.js và nên đọc toàn bộ mã nguồn đã release để hiểu được rõ ràng vấn đề hơn.

Blog: JavaScript - Debounce Function là gì?

Tham khảo

  • Javascript – Giới thiệu về Debounce Function
  • Debounce and Throttle: a visual explanation
0