Đo lường thời gian thực hiện hàm trong Javascript một cách thanh lịch

811

Bài viết được sự cho phép bởi tác giả Sơn Dương

Vấn đề

Trong quá trình phát triển ứng dụng hoặc đang vận hành nó trong môi trường production sẽ có lúc phát sinh ra nhiều vấn đề mà chúng ta không lường trước được. Khi ứng dụng được tung ra thị trường, chắc chắn lượng người dùng sẽ nhiều lên từng ngày, từ đó tạo ra sự đa dạng về hành vi lẫn dữ liệu người dùng thu thập được. Chúng kết hợp với nhau tạo ra nhiều trường hợp phát sinh lỗi mà quá trình kiểm thử trước đó không hề phát hiện ra.

Một trong số đó có thể kể đến như là tốc độ phản hồi API bỗng trở nên chậm chạp mặc dù lượng người sử dụng là không nhiều. Khi kiểm tra lại tính năng gây ra tình trạng đó, có vẻ bạn phát hiện ra hoặc đang nghi ngờ một hàm nào đó chiếm lượng thời gian xử lý lớn. Để chắc chắn là mình đúng thì tất nhiên phải đo được xem cụ thể là hàm đó tốn bao nhiêu thời gian để xử lý.

Có nhiều cách để biết hàm nào đang gây chậm hệ thống. Các ứng dụng APM với khả năng kiểm soát và chuẩn đoán ứng dụng của bạn rất tốt để kịp thời cảnh báo vấn đề. Một vài dịch vụ APM có tính năng thu thập hoặc cảnh báo theo thời gian thực nếu như chúng phát hiện ra một hàm nào đó đang mất quá nhiều thời gian để thực hiện, hoặc thời gian phản hồi của API là quá lâu. Nhưng đổi lại, giá thành của chúng không hề rẻ, hoặc nếu có miễn phí thì nhiều hạn chế sẽ phát sinh. Vì lẽ đó không phải ai cũng có điều kiện sử dụng APM, đó là lúc bạn cần đến một cách “xôi thịt” hơn nhưng lại hoàn toàn miễn phí mà ai cũng có thể tiếp cận được.

  5 thủ thuật hay với Javascript Promise bạn cần biết!

  Một số kinh nghiệm làm việc với mảng trong JavaScript dành cho bạn

Đo lường thời gian thực hiện hàm trong Javascript

Date.now()console.time()performance.now()process.hrtime()… là các hàm thường hay được sử dụng để đo khoảng thời gian mà hàm thực hiện. Về cơ bản, chỉ cần kẹp giữa hàm cần đo bằng chúng thì sẽ tính được thời gian thực thi.

Ví dụ sau dử dụng Date để tính thời gian thực thi bằng cách trừ hai khoảng thời gian trước và sau khi hàm được chạy.

const start = Date.now();

await functionToBeMeasured();

const end = Date.now();
console.log(`Execution time: ${end - start} ms`);

Ngoài ra, đơn giản hơn thì dùng console.time:

console.time('executionFunctionToBeMeasured');

await functionToBeMeasured();

console.timeEnd('executionFunctionToBeMeasured');

console.time không mang lại độ chính xác cao. Nếu muốn độ chính xác theo phần nghìn giây, hãy sử dụng hàm mạnh mẽ hơn với performance.now() hoặc process.hrtime() nếu sử dụng Node.js.

const start = performance.now();

await functionToBeMeasured();

const end = performance.now();
console.log(`Execution time: ${end - start} ms`);

Sau khi biết cách đo thời gian, một vấn đề khác nảy sinh đó chính là cần phải đặt hàm cần đo kẹp giữa hai hàm tính thời gian. Điều này gây ra sự xáo trộn mã trong dự án của bạn, phải sửa nhiều hơn, mã trở nên cồng kềnh hơn và khó kiểm soát.

Việc làm Javascript lương cao trên TopDev

Ý tưởng lúc này là viết một hàm để gói gọn tính năng đo thời gian thực thi. Đầu vào là một hàm cần tính còn đầu ra là kết quả của hàm đó cùng với thời gian thực thi. Một hàm gì đó trông giống như dưới đây:

calcExecuteTime(functionToBeMeasured);

functionToBeMeasured đôi khi là hàm có cả tham số, vì thế hãy sửa lại một chút.

calcExecuteTime(() => functionToBeMeasured(arg1, arg2...));

Nếu trong functionToBeMeasured có sử dụng this, hãy thêm một tham số context để nhận this từ nơi gọi.

calcExecuteTime(context, () => functionToBeMeasured(arg1, arg2...));

Cuối cùng, nên thêm một tham số name để xác định được cuộc gọi hàm từ đâu.

calcExecuteTime(name, context, () => functionToBeMeasured(arg1, arg2...));

Hàm calcExecuteTime có thể giống như sau:

const calcExecuteTime = async (name, context, fn) => {
  const start = process.hrtime();
  const result = await fn.call(context);
  const stop = process.hrtime(start);
  const executeTime = (stop[0] * 1e9 + stop[1]) / 1e9;
  console.log(`${name} execution time: ${executeTime}s`);
  return result;
};

Bây giờ hãy thử gọi hàm xem:

calcExecuteTime("Execute ToBeMeasured", this, () => functionToBeMeasured(arg1, arg2...));
// Execute ToBeMeasured execution time: 0.000178s

Bạn cũng có thể sửa lại hàm tùy vào mục đích, ví dụ thay vì console.log thì hãy logging lại thời gian thực hiện hàm vào đâu đó. Hoặc nâng cấp hàm lên ví dụ như thêm một cờ bật/tắt tính năng tính thời gian.

Bài viết gốc được đăng tải tại vntalking.com

Có thể bạn quan tâm:

Xem thêm các việc làm IT hấp dẫn tại TopDev