Improve Responsiveness to User Interactions

nuko_suke

nuko_suke

Posted on January 16, 2023

Improve Responsiveness to User Interactions

I will share with you a story of performance improvement at my site.

Prerequisite

I must tell you about prerequisite like npm dependencies before introducing the case.

The website that I improved is Instagram Hashtag Translator .
It helps us to translate Instagram hashtags.

Instagram Hashtag Translator

The technical conditions are as follows.

  • npm dependencies
    • React v18.2.0
    • Next.js v13.0.5
  • Hosting
    • Vercel

I measured the time it took to process the JavaScript when clicking the translate button.

const clickHandler = () => {
  const start = performance.now();
  // ...
  // ...processing
  // ...
  console.log(Math.ceil((performance.now() - start) * 100) / 100);
}
Enter fullscreen mode Exit fullscreen mode

Before Improvement

const clickHandler = async () => {
  // Dynamic import
  const { default: sendGaEvent } = await import(
    '~/lib/analytics/sendGaEvent'
  );
  // ...
  // Send analytics data
  sendGaEvent(data);
  // ...
  // Dynamic import
  const { default: translate } = await import('~/infra/translate');
  // ...
}
Enter fullscreen mode Exit fullscreen mode

There are notable 2 points.

  1. Import modules when user clicked the button
  2. Send analytics data

1. Import modules when user clicked the button

By using dynamic import , we can reduces the code needed on the initial load of our application.

It likes good idea, but there also is problem.
Dynamic import gives users bad responsiveness because it is not until they clicked the button that browsers load modules.

2. Send analytics data

Is it important to send analytics data?
This answer is no.

We want to have JavaScript main thread process important tasks
like translating words as much as possible.

After Improvement

I improve the performance by using idle-task .

https://github.com/hiroki0525/idle-task

import { setIdleTask, forceRunIdleTask } from 'idle-task';

// point 1
const importSendGaEventTaskId = setIdleTask(
  () => import('~/common/analytics/sendGaEvent')
);
const importTranslateTaskId = setIdleTask(() => import('~/infra/translate'));

const clickHandler = async () => {
  // point 1
  const { default: sendGaEvent } = await forceRunIdleTask(
    importSendGaEventTaskId
  );
  // ...
  // point 2
  setIdleTask(() => sendGaEvent(data));
  // ...
  // point 1
  const { default: translate } = await forceRunIdleTask(importTranslateTaskId);
  // ...
}
Enter fullscreen mode Exit fullscreen mode

point 1

I prepare browsers to load modules during their idle periods

const importSendGaEventTaskId = setIdleTask(
  () => import('~/common/analytics/sendGaEvent')
);
const importTranslateTaskId = setIdleTask(() => import('~/infra/translate'));
Enter fullscreen mode Exit fullscreen mode

setIdleTask can register tasks which will be executed when browsers are idle.

  const { default: sendGaEvent } = await forceRunIdleTask(
    importSendGaEventTaskId
  );
  const { default: translate } = await forceRunIdleTask(importTranslateTaskId);
  // ...
}
Enter fullscreen mode Exit fullscreen mode

We can get the results by using forceRunIdleTask .

It will not always be idle if browsers are busy.
forceRunIdleTask gives us the result from cache when the task had been executed, otherwise idle-task run immediately.

point 2

Send analytics data during a browser's idle periods

setIdleTask(() => sendGaEvent(data));
Enter fullscreen mode Exit fullscreen mode

As I have already told you, setIdleTask help us to execute tasks when browsers are idle.

It seems to have solved all my problems!

Result

Before(ms) After(ms)
1st 1110.8 1048.8
2nd 1191.2 1068.9
3rd 1070.4 1028.4
4th 1084.8 1023.2
5th 1099.4 1080.8
average 1111.3 1050.0

That's an improvement of about 5.5% 🎉.
In fact, the main translation process itself takes about 1,000 ms , so if we consider the non-main process, the improvement is about 55%.

In absolute terms, this is an improvement of 50 to 60 ms, which may not seem like much numerically at first glance, but an improvement of 50 to 60 ms is significant.

In light of the RAIL model, users will feel comfortable when the execution time is roughly 100 ms or less (50 ms is recommended as a guideline).

Considering this, an improvement of 50 to 60 ms would be significant .

Conclusion

I improved the performance of Instagram Hashtag Translator by using idle-task .

https://github.com/hiroki0525/idle-task

If you found this helpful, please help someone else by sharing it or tagging someone in the comments!

Thanks!

💖 💪 🙅 🚩
nuko_suke
nuko_suke

Posted on January 16, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

This or that?
webdev This or that?

December 6, 2023