Measuring the Execution Time of Functions in JavaScript in a Graceful Way

Measuring the Execution Time of Functions in JavaScript in a Graceful Way

Problem

During the development or operation of an application, unforeseen issues may arise. Once your application is released to the market, the number of users is likely to increase every day, resulting in a wide range of user behaviors and collected user data. These combinations can create various error scenarios that were not caught during the previous testing processes.

One of them may be that the API response suddenly becomes slow, even with a small number of users. When checking the feature causing this issue, it seems that you have discovered or are suspecting that a certain function is taking up a significant amount of processing time. To make sure you're right, of course, you need to measure how much time that function takes to execute.

There are many ways to identify which function is slowing down your system. APM applications with good control and diagnosis capabilities can timely alert you to issues. Some APM services have real-time data collection or alerting features if they detect that a function is taking too long to execute or if the API response time is too long. However, these services are not cheap, or if they are free, they come with limitations. That's why not everyone can afford to use APM. That's when you need a "cheaper" way that is completely free and accessible to everyone.

Measuring the Execution Time of Functions in JavaScript

Date.now(), console.time(), performance.now(), process.hrtime(), these are commonly used functions for measuring the time a function takes to execute. Basically, you just need to wrap the function you want to measure with these functions, and the execution time will be calculated.

The following example uses Date to measure the execution time by subtracting the two timestamps before and after the function is called.

const start = Date.now();

await functionToBeMeasured();

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

Alternatively, you can use console.time for a simpler approach:

console.time('executionFunctionToBeMeasured');

await functionToBeMeasured();

console.timeEnd('executionFunctionToBeMeasured');

console.time does not provide high accuracy. If you want sub-millisecond accuracy, you can use more powerful functions like performance.now() or process.hrtime() if you are using Node.js.

const start = performance.now();

await functionToBeMeasured();

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

After knowing how to measure the execution time, another issue arises: we need to wrap the function to be measured between the two time measurement functions. This causes code disruption in your project, requiring more modifications, making the code more cumbersome and difficult to control.

The idea here is to write a function to encapsulate the time measurement feature. The input is the function to be measured, and the output is the result of that function along with the execution time. Something like this:

calcExecuteTime(functionToBeMeasured);

Sometimes, functionToBeMeasured takes arguments, so let's make a slight modification:

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

If functionToBeMeasured uses this, add a context parameter to receive this from the caller:

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

Lastly, it is a good idea to add a name parameter to identify where the function is called from:

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

The calcExecuteTime function can look like this:

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

Now let's try calling the function:

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

You can also modify the function according to your needs, for example, instead of logging the execution time using console.log, you can log it to somewhere else. Or enhance the function by adding a toggle flag to enable/disable the measuring feature.

References:

or
* The summary newsletter is sent every 1-2 weeks, cancel anytime.
Author

Hello, my name is Hoai - a developer who tells stories through writing ✍️ and creating products 🚀. With many years of programming experience, I have contributed to various products that bring value to users at my workplace as well as to myself. My hobbies include reading, writing, and researching... I created this blog with the mission of delivering quality articles to the readers of 2coffee.dev.Follow me through these channels LinkedIn, Facebook, Instagram, Telegram.

Did you find this article helpful?
NoYes

Comments (1)

Leave a comment...
Avatar
Tuan Nguyen1 year ago
Thử chạy cái hàm đó trong 1_000_000 rồi tính trung bình sau đó đem so với chạy cái hàm đó 1 lần. Kết quả sẽ bất ngờ đó. Đó là cơ chế của node
Reply
Avatar
Xuân Hoài Tống1 year ago
Bạn ơi đó là cơ chế gì vậy ạ?