Question

How to Measure Function Execution Time in JavaScript

javascriptprofiling

Question

I need to measure how long a function takes to execute in milliseconds.

Originally, this was commonly done with new Date().getTime(), but today the more appropriate standard approach is to use the performance.now() API.

What is the correct way to measure execution time in JavaScript, and when should performance.now() be preferred over older date-based approaches?

Short Answer

By the end of this page, you will understand how to measure execution time in JavaScript, why performance.now() is usually the best choice for benchmarking code, how to use it correctly, and what mistakes to avoid when timing functions.

Concept

Measuring execution time means recording a timestamp before some code runs, recording another timestamp after it finishes, and subtracting the two values.

In JavaScript, the modern tool for this is performance.now().

const start = performance.now();
// code to measure
const end = performance.now();
console.log(`Time taken: ${end - start} ms`);

Why performance.now() matters

performance.now() is designed for performance measurements:

  • It returns a time value in milliseconds.
  • It has better precision than older date-based methods.
  • It is intended for measuring short durations like function execution.
  • It is not affected in the same way as wall-clock time APIs used for dates and times.

Older approaches like new Date().getTime() or Date.now() return the current calendar time in milliseconds since the Unix epoch. They are useful for timestamps, but less ideal for precise benchmarking.

What you are really measuring

When you time a function, you are measuring:

  • JavaScript execution time
  • Possible engine optimizations or warm-up effects
  • Time spent in loops, calculations, and synchronous operations

If your function does asynchronous work, such as fetch, setTimeout, or database calls, you need a different timing pattern because the function may return before the work is finished.

Mental Model

Think of timing code like using a stopwatch.

  • Press start right before the function runs.
  • Press stop right after it finishes.
  • The difference is the duration.

performance.now() is like a stopwatch built specifically for measuring elapsed time.

Date.now() is more like looking at a wall clock. It tells you the current time, which is useful, but it is not the best tool when you care about small, precise durations.

Take Quiz

Syntax and Examples

Basic syntax

const start = performance.now();
doSomething();
const end = performance.now();

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

Example: measuring a synchronous function

function calculateSum() {
  let total = 0;
  for (let i = 0; i < 1000000; i++) {
    total += i;
  }
  return total;
}

const start = performance.now();
const result = calculateSum();
const end = performance.now();

console.log("Result:", result);
console.log(`Execution time: ${(end - start).toFixed(3)} ms`);

In this example:

  • start stores the time before the function runs.
  • calculateSum() executes.
  • end stores the time after execution.
  • end - start gives the elapsed time.

Older alternative: Date.now()

const start = Date.now();
doSomething();
const end = Date.now();

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

This works, but performance.now() is usually better for measuring code performance.

Measuring an async function

async function loadData() {
  return new Promise(resolve => {
    setTimeout(() => resolve("done"), 300);
  });
}

async function measure() {
  const start = performance.now();
  const result = await loadData();
  const end = performance.now();

  console.log(result);
  console.log(`Execution time: ${(end - start).toFixed(3)} ms`);
}

measure();

For asynchronous code, place the end measurement after await so the full operation is included.

Step by Step Execution

Consider this example:

function greetManyTimes() {
  let text = "";
  for (let i = 0; i < 3; i++) {
    text += "Hello ";
  }
  return text.trim();
}

const start = performance.now();
const message = greetManyTimes();
const end = performance.now();

console.log(message);
console.log(end - start);

Step by step

  1. greetManyTimes is defined.
  2. start stores the current high-resolution timestamp.
  3. greetManyTimes() runs.
  4. Inside the function:
    • text starts as an empty string.
    • The loop runs 3 times.
    • Each time, "Hello " is added.
    • The function returns "Hello Hello Hello".
  5. message receives the returned string.
  6. end stores the timestamp after the function has finished.
  7. end - start is calculated.
  8. The result is the elapsed time in milliseconds.

Example trace

If:

  • start = 1250.100
  • end = 1250.450

Then:

elapsed = 1250.450 - 1250.100 = 0.350 ms

That means the function took about 0.35 milliseconds.

Real World Use Cases

Measuring execution time is useful in many real programs.

Performance checks

You may want to compare two implementations:

  • a for loop vs Array.map()
  • two sorting strategies
  • a cached function vs a non-cached one

API and async timing

You might measure how long an API request takes:

async function fetchUsers() {
  const start = performance.now();
  const response = await fetch('/api/users');
  const data = await response.json();
  const end = performance.now();

  console.log(`Request took ${(end - start).toFixed(2)} ms`);
  return data;
}

UI performance

In frontend apps, developers measure:

  • render time
  • event handler duration
  • animation performance
  • expensive calculations triggered by user input

Backend scripts and tools

In Node.js scripts, timing helps with:

  • file processing
  • data transformation
  • report generation
  • batch jobs
Take Quiz

Real Codebase Usage

In real projects, developers usually do more than just print a number.

Common patterns

1. Wrap timing in a helper

function measure(label, fn) {
  const start = performance.now();
  const result = fn();
  const end = performance.now();
  console.log(`${label}: ${(end - start).toFixed(3)} ms`);
  return result;
}

const value = measure("sum calculation", () => {
  let total = 0;
  for (let i = 0; i < 1000000; i++) total += i;
  return total;
});

This keeps timing code reusable.

2. Use try...finally when timing important code

function measureSafe(fn) {
  const start = performance.now();
  try {
    return fn();
  } finally {
    const end = performance.now();
    console.log(`Finished in ${(end - start).toFixed(3)} ms`);
  }
}

This ensures timing is logged even if an error happens.

3. Time async helpers

async function measureAsync(label, fn) {
  const start = performance.now();
  try {
    return await fn();
  } finally {
    const end = performance.now();
    console.log(`${label}: ${(end - start).toFixed(3)} ms`);
  }
}

4. Benchmark multiple runs

A single run can be misleading. Developers often run code many times and average the result.

function benchmark(fn, iterations = 1000) {
  const start = performance.now();
  for (let i = 0; i < iterations; i++) {
    fn();
  }
  const end = performance.now();
  return (end - start) / iterations;
}

This helps reduce noise.

Common Mistakes

1. Using Date for precise benchmarking

Broken approach:

const start = new Date().getTime();
doSomething();
const end = new Date().getTime();

This may work, but it is not the preferred modern choice for measuring short durations.

Use instead:

const start = performance.now();
doSomething();
const end = performance.now();

2. Timing async code incorrectly

Broken example:

const start = performance.now();
fetch('/api/data');
const end = performance.now();

console.log(end - start);

This only measures how long it took to start the request, not complete it.

Correct version:

const start = performance.now();
await fetch('/api/data');
const end = performance.now();

3. Measuring code only once

One execution can be affected by:

  • engine warm-up
  • background processes
  • caching
  • system load

Better approach: run the code several times and compare averages.

4. Including unrelated work in the measurement

Broken example:

const start = performance.now();
const result = heavyCalculation();
console.log(result);
const end = performance.now();

This includes console.log, which can affect the timing.

Better:

const start = performance.now();
const result = heavyCalculation();
const end = performance.now();

console.log(result);
console.log(end - start);

5. Comparing tiny operations directly

Very small operations can complete too quickly to compare reliably in one run. Repeat them many times before drawing conclusions.

Comparisons

ApproachBest usePrecisionGood for benchmarking?Notes
performance.now()Measuring elapsed execution timeHighYesPreferred modern API
Date.now()Current timestampLowerSometimesFine for coarse timing
new Date().getTime()Current timestampLowerSometimesOlder style, same idea as Date.now()
console.time() / console.timeEnd()Quick debugging in developmentVaries by environmentSometimesConvenient but less flexible

performance.now() vs Date.now()

  • Use performance.now() when you care about measuring code duration.
  • Use Date.now() when you want the current timestamp for logs, records, or comparisons with real-world time.

performance.now() vs console.time()

console.time() is handy for quick experiments:

console.time("task");
doSomething();
console.timeEnd("task");

This is convenient, but performance.now() gives you more control because you can store, calculate, return, and aggregate the timing values yourself.

Take Quiz

Cheat Sheet

Quick reference

Preferred timing API

const start = performance.now();
// code here
const end = performance.now();
const duration = end - start;

Older alternatives

Date.now();
new Date().getTime();

Use these for timestamps, not ideal for precise benchmarks.

Async timing

const start = performance.now();
await someAsyncTask();
const end = performance.now();

Quick debug timing

console.time("myTask");
doSomething();
console.timeEnd("myTask");

Good habits

  • Use performance.now() for elapsed time
  • Measure only the code you care about
  • Repeat benchmarks multiple times
  • Keep logging outside the timed section when possible
  • Use await for async work before stopping the timer

Watch out for

  • timing async code without await
  • comparing tiny operations from a single run
  • including setup or output code accidentally

FAQ

Should I use performance.now() or Date.now() in JavaScript?

Use performance.now() for measuring execution time. Use Date.now() for real timestamps such as logging when something happened.

Is new Date().getTime() still valid?

Yes, it still works, but it is an older style. For performance measurement, performance.now() is the better modern choice.

Does performance.now() return milliseconds?

Yes. It returns a time value in milliseconds, typically with fractional precision.

How do I measure an async function's execution time?

Record the start time before the async call and the end time after await completes.

Why do benchmark results change between runs?

Because of factors like JavaScript engine optimization, caching, system load, and background tasks. That is why multiple runs are better than one.

Can I use console.time() instead?

Yes, for quick debugging. But performance.now() is usually better when you want to store or process timing values in your own code.

Take Quiz

Mini Project

Description

Build a small timing utility that measures how long different functions take to run. This project demonstrates how to use performance.now() in a reusable way instead of rewriting timing code every time.

Goal

Create a reusable function that measures synchronous and asynchronous tasks and prints their execution times in milliseconds.

Requirements

  • Create a function to measure a synchronous callback.
  • Create a function to measure an asynchronous callback.
  • Display the task name and execution time.
  • Test the utility with one CPU-based function and one async function.
Take Quiz

Keep learning

Related questions

View more