hiroki0525/idle-task

How to bind the task with an user event

onlinebizsoft opened this issue · 7 comments

There are some common cases like

  • A mobile menu to be initiated and used when user click on the button
  • A search JS overlay need to be initiated and when user input the search, it will start interact with users immediately

I'm looking a way to optimize some UI JS for those and found this together with
https://github.com/GoogleChromeLabs/idlize

Both are nice idea and exactly what I'm approaching. However in both cases, I can not see an interface API which allow to do these when CPU is idle while allow the initialization can be triggered immediately if users interact (and it was not initiated).

Do you have any idea? @hiroki0525 @yuchi @joeinnes @m5r

I have a good idea.
You can use forceRunIdleTask .

Look at the following example.

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

// importing the module will be run during the browser's idle periods.
const taskKey = setIdleTask(() => import('./yourFunction'));

const button = document.getElementById('button');
button.addEventListener('click', async () => {
    // default export
    const { default: yourFunction } = await forceRunIdleTask(taskKey);
    yourFunction();
})

In this example, we will see 2 cases when the user clicks the button.

  1. Completed to loading './yourFunction' .
  2. Not completed to loading './yourFunction' .

forceRunIdleTask is useful in both cases.

1. Completed to loading './yourFunction' .

No problem. forceRunIdleTask returns the result of importing './yourFunction' which was executed when the browser was idle.

2. Not completed to loading './yourFunction' .

This is your concern, isn't it?
In this case, forceRunIdleTask make the browser import './yourFunction' immediately .

In conclusion, the solution is to use forceRunIdleTask .

Thanks for great feedback, it sounds promising. I didnt know about forceRunIdleTask because of no document.

I have some further points

  1. Will the task be skipped from the queue if it was executed by force run?
  2. Why dont you wrap these function into an object with proper interface, code would be much cleaner and easier to use?

I am in the process of documenting it.

https://hiroki0525.github.io/idle-task/category/api

  1. Will the task be skipped from the queue if it was executed by force run?

Yes. The task will be removed from the queue, but the result will be cached.
The cache also will be removed automatically when it isn't used anywhere.

const taskKey = setIdleTask(() => import('./yourFunction'));
// The task will be removed from the queue.
const result1 = await forceRunIdleTask(taskKey);
// The result from cache.
const result2 = await forceRunIdleTask(taskKey);
result1 === result2 // => true
  1. Why dont you wrap these function into an object with proper interface, code would be much cleaner and easier to use?

This is a good point. I think so too, however I guess that we will not be able to benefit from Tree shaking.
Imagine the object which has all the functions like below.

import { setIdleTask } from 'idle-task';

const task = setIdleTask(yourFunction);
const result = await task.forceRunIdleTask();
task.cancelIdleTask();

In this case, we use only forceRunIdleTask and cancelIdleTask , but the bundled file perhaps includes other functions code because task object has them.

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

const taskKey = setIdleTask(yourFunction);
const result = await forceRunIdleTask(taskKey);
cancelIdleTask(taskKey);

This is tree-shakeable. The bundled file only has forceRunIdleTask and cancelIdleTask code.

On the other hand, the former is easier to use.
I'm on the fence and may change the interface in the future.

@hiroki0525 I understand the point of keeping Tree shaking principle however it is about trading-off, I think the performance gain in this case is too small.
Even it may lead to redundant code or longer code when users need to use this in multiple places in the project.

In fact, more than 90% of projects will need to use almost available methods so it would be good to make a good code structure, object-oriented. Even you can still allow dynamic import for optional features via constructor injection

Thanks for your feedback, but I'm sorry that I don't fully understand what you are saying.

In fact, more than 90% of projects will need to use almost available methods so it would be good to make a good code structure, object-oriented. Even you can still allow dynamic import for optional features via constructor injection

Could you tell me more about it? Are there any related articles or code examples?

@hiroki0525 I guess you mean about

allow dynamic import for optional features via constructor injection

I don't have an example but the idea is

class Something {
constructor(param1, param2,.....) {
if (param1) import "lib/param1";
if (param2) import "lib/param3";
.....
feature1 = true;
feature2 = true;
}

//other methods implementations go here and work according which feature is enabled
}

Correct me if I'm wrong? Anyway I think the cost for including all common user cases into a wrapper is not big to fear.

I understand. I can't say for sure when I will do it, but I will change to a better interface.