DEV Community

Cover image for Optimise performance in Javascript: Part 2
shelly agarwal
shelly agarwal

Posted on • Edited on

Optimise performance in Javascript: Part 2

Hey developers! Welcome to my second blog on JavaScript performance.

In this post, Beforing walking you through optimizing long tasks in JavaScript in this post to prevent performance bottlenecks, I recommend reading the previous post Part 1 of performance in Javascript

We'll dive into DevTools' Performance tab to analyze slow-performing functions, identify bottlenecks, and improve execution time effectively.
Letโ€™s explore how to make your JavaScript code more efficient!

In Login/index.tsx file, I have added a long task

function heavyComputation() {
    let total = 0;
    for (let i = 0; i < 1e9; i++) {  // Heavy loop
      total += i;
    }
    console.log("Computation done:", total);
  }

    useEffect(() => { 
      heavyComputation();  // UI freezes while processing
      console.log("Task complete!")
    },[])
Enter fullscreen mode Exit fullscreen mode

Image_description

On clicking "Event Log" tab, we can see "Function call" and on expanding it, it shows heavyComputation details (Look carefully at right side, index.tsx:203:12 Same you can see on the right side) -> It is showing exact line from where this heavy operation is happening.
Total Time: ~1023ms

first_description

Now we can see below ๐Ÿ‘‡, for loop is taking almost 2 sec to complete the operation and it is blocking our main thread.๐Ÿ˜ตโ€๐Ÿ’ซ

second_img

๐Ÿ”ด Issues:

  1. The UI freezes while the computation runs.
  2. No interactions or rendering updates happen until the task completes.

๐ŸŸข Solution:

Create a worker.js file under public folder๐Ÿ‘‡

1_Image_description

self.onmessage = () => {
  let total = 0;
  for (let i = 0; i < 1e9; i++) {
    // Heavy computation
    total += i;
  }
  self.postMessage(total); // Send result back
};
Enter fullscreen mode Exit fullscreen mode

Import worker.js and set result in the component whenever it is ready

 useEffect(() => {
    const worker = new Worker(new URL('./public/worker.js', 
import.meta.url));
    worker.onmessage = (e) => {
      setResult(e.data);
      console.log('Task complete!', e.data);
    };

    worker.postMessage('start'); // Start computation
    return () => worker.terminate(); // Cleanup
  }, []);

Enter fullscreen mode Exit fullscreen mode

Now, retry performance recording and see the result -
Image_0description

๐Ÿ’ก Benefits:

  1. Total time reduced significantly.
  2. UI remains responsive (no blocking of the main thread).
  3. Computation runs in a separate thread, preventing UI freezes.
  4. Better user experience and faster interactions.

Alternative solution instead of using "worker"? ๐Ÿ’ก
If Web Workers arenโ€™t an option, we can also chunk the computation to run only during idle time. (Using requestIdleCallback approach)

useEffect(() => {
  let total = 0;
  let i = 0;

  function processChunk(deadline) {
    while (i < 1e9 && deadline.timeRemaining() > 0) {
      total += i;
      i++;
    }

    if (i < 1e9) {
      requestIdleCallback(processChunk);
    } else {
      console.log('Computation done:', total);
    }
  }

  requestIdleCallback(processChunk);
}, []);
Enter fullscreen mode Exit fullscreen mode

What deadline.timeRemaining means in requestIdleCallback?
deadline.timeRemaining() is a method inside requestIdleCallback that tells us how much time (in milliseconds) is left in the current idle period. It helps split heavy tasks into smaller chunks that run only when the browser is idle, keeping the UI responsive.

โœ” Key Takeaways from this blog?

  1. Use Web Workers for CPU-heavy tasks ๐Ÿง 
  2. Use requestIdleCallback for lower-priority tasks โœ”๏ธ
  3. Never block the main thread in Javascript/ReactJS! ๐Ÿ‘

Comment down your go to solution ๐Ÿ‘‡

Top comments (0)