yuchi / idle-task

Improve your website performance by executing JavaScript during a browser's idle periods.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

idle-task

idle-task

npm version npm bundle size Test License: MIT semantic-release: angular

Improve your website performance by executing JavaScript during a browser's idle periods.

Table of Contents

What is difference between idle-task and requestIdleCallback

Why you should use idle-task instead of requestIdleCallback ?

Manage tasks priority

import { setIdleTask } from 'idle-task';
setIdleTask(yourLowPrioryFunction, { priority: 'low' });
setIdleTask(yourHighPrioryFunction, { priority: 'high' });

Get result by using Promise based API

import { getResultFromIdleTask } from 'idle-task';
// get result asynchronously
const result = await getResultFromIdleTask(yourFunction);

Cache

import { setIdleTask, waitForIdleTask } from 'idle-task';
const taskId = setIdleTask(yourFunction);
const result1 = await waitForIdleTask(taskId);
// from cache
const result2 = await waitForIdleTask(taskId);

Optimize executing tasks

import { setIdleTask } from 'idle-task';
setIdleTask(longTask);
// these functions will be executed during next browser's idle time.
setIdleTask(shortTask);
setIdleTask(shortTask);

Analyze tasks execution time

import { setIdleTask, configureIdleTask } from 'idle-task';
configureIdleTask({ debug: true })
// output the execution time to the web console.
setIdleTask(yourFunction1);

Install

npm i idle-task

Quick Start

The simplest way is to use setIdleTask .

import { setIdleTask } from 'idle-task';

const sendAnalyticsData = () =>
        console.log("send analytics data during a browser's idle periods.");
setIdleTask(sendAnalyticsData);

If you want to get the result of a task, please use waitForIdleTask .

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

const taskId = setIdleTask(yourFunction);
const result = await waitForIdleTask(taskId);

API

setIdleTask

const sendAnalyticsData = () => console.log("send analytics data");
const options = {
    priority: 'high',
    cache: false
};
const taskId = setIdleTask(sendAnalyticsData, options);

idle-task has a FIFO(First-In-First-Out) queue.

setIdleTask enqueues a task which idle-task will dequeue and run when the browser is idle.

setIdleTask returns task id which is necessary for cancelIdleTask , isRunIdleTask and waitForIdleTask.

I recommend less than 50 ms to execute a task because of RAIL model . If you want to know how long did it take to finish a task, please use debug mode .

setIdleTask can also be set options as below.

priority?: 'low' | 'high'

You can run a task preferentially using priority: 'high' (default is false) option. setIdleTask adds it to the head of the queue.

cache?: boolean

This option is to improve performance.

idle-task caches the results of tasks by default .

I recommend to set false if you don't need the result of idle task.

waitForIdleTask will return undefined when cache is false .

import {waitForIdleTask} from "idle-task";

const sendAnalyticsData = (): void => {
    console.log("send analytics data")
};
// Recommend: sendAnalyticsData result is not needed
setIdleTask(sendAnalyticsData, {cache: false});

const generateRandomNumber = () => Math.floor(Math.random() * 100);
const taskId = setIdleTask(generateRandomNumber, {cache: false});
// result is undefined
const result = await waitForIdleTask(taskId);

waitForIdleTask

const generateRandomNumber = () => Math.floor( Math.random() * 100 );
const taskId = setIdleTask(generateRandomNumber);
const randomNumber = await waitForIdleTask(taskId, {
    cache: false
});

You can get the result of the task by using waitForIdleTask .

waitForIdleTask can also be set options as below.

cache?: boolean

idle-task caches the results of tasks by default .

const generateRandomNumber = () => Math.floor( Math.random() * 100 );
const taskId = setIdleTask(generateRandomNumber);
const firstRandomNumber = await waitForIdleTask(taskId);
// this result from cache
const secondRandomNumber = await waitForIdleTask(taskId);
// same objects
console.log(Object.is(firstRandomNumber, secondRandomNumber));
// => true

If you get the result of a task only once, please set { cache: false } . This will improve memory in JavaScript.

const generateRandomNumber = () => Math.floor( Math.random() * 100 );
const taskId = setIdleTask(generateRandomNumber);
// delete cache
const firstRandomNumber = await waitForIdleTask(taskId, { cache: false });
// this is undefined
const secondRandomNumber = await waitForIdleTask(taskId);
// not same objects
console.log(Object.is(firstRandomNumber, secondRandomNumber));
// => false

timeout?: number

waitForIdleTask maybe wait for the task eternally because it will be finished when the browser is idle. timeout option can prevent it.

const generateRandomNumber = () => Math.floor( Math.random() * 100 );
const taskId = setIdleTask(generateRandomNumber);
try {
    const firstRandomNumber = await waitForIdleTask(taskId, { timeout: 1000 });
} catch (e) {
    if (e instanceof WaitForIdleTaskTimeoutError) {
        console.error('this is timeout error')
    }
}

In this case, waitForIdleTask will throw WaitForIdleTaskTimeoutError if the task can't be finished within 1000 ms.

getResultFromIdleTask

const generateRandomNumber = () => Math.floor( Math.random() * 100 );
const randomNumber = await getResultFromIdleTask(generateRandomNumber, {
    priority: 'high',
    timeout: 3000,
});

// same
const taskId = setIdleTask(generateRandomNumber, { priority: 'high' });
const randomNumber = await waitForIdleTask(taskId, { timeout: 3000, cache: false });

You can get the result by using getResultFromIdleTask if you don't need the task id.

getResultFromIdleTask can also be set options which is SetIdleTaskOptions.priority and WaitForIdleTaskOptions.timeout .

cancelIdleTask

const taskId = setIdleTask(() => console.log("task will be canceled."));
cancelIdleTask(taskId);

You can stop to run a task by using cancelIdleTask if it is not executed.

cancelAllIdleTasks

setIdleTask(() => console.log("task 1 will be canceled."));
setIdleTask(() => console.log("task 2 will be canceled."));
setIdleTask(() => console.log("task 3 will be canceled."));
cancelAllIdleTasks();

You can stop to run all tasks by using cancelAllIdleTasks if they are not executed.

isRunIdleTask

const taskId = setIdleTask(() => console.log("task"));
const isRun = isRunIdleTask(taskId);
if (isRun) {
  console.log("the task was completed");
}

deprecated : This function will be replaced alternative one.

You can know whether the task is executed or not by using isRunIdleTask .

configureIdleTask

configureIdleTask({
  interval: 1000, // ms
  debug: false,
  timeout: 3000,
});

configureIdleTask configures idle-task . You can set properties as below.

interval?: number

idle-task checks tasks which was registered by setIdleTask during a browser's idle periods, so they will not always be executed .

Please set interval if you want to guarantee to run tasks as much as possible.

Even if the browser is not idle, idle-task checks tasks every 1000 ms when interval is 1000 and will execute tasks without negative impact on performance.

debug?: boolean

If debug is true, you can know how long did it take to finish the task via the web console.

I recommend less than 50 ms to execute a task because of RAIL model .

The default is process.env.NODE_ENV === 'development' .

timeout?: number

This option configures timeout of waitForIdleTask and getResultFromIdleTask as default setting.

configureIdleTask({ timeout: 3000 });

const taskId = setIdleTask(yourFunction);
// timeout is 3000
const result = await waitForIdleTask(taskId);

// timeout is 5000 if you set timeout as option
const result = await waitForIdleTask(taskId, { timeout: 5000 });

cache?: false

This option configures cache of setIdleTask as default setting. Default is true . You should set false if it is make sure not to need all the results of tasks when using setIdleTask .

configureIdleTask({ cache: false });

// cache option is false
setIdleTask(yourFunction);
// same
setIdleTask(yourFunction, { cache: false })

// cache is true if you set cache as option
setIdleTask(yourFunction, { cache: true });

Recipes

Vanilla JS

import { setIdleTask } from 'idle-task';

// this module is loaded during a browser's idle periods because it is not important for UI.
const taskId = setIdleTask(() => import('./sendAnalyticsData'))

const button = document.getElementById('button');
button.addEventListener('click', async () => {
    const { default: sendAnalyticsData } = await waitForIdleTask(taskId);
    // Send analytics data to server when the browser is idle.
    setIdleTask(sendAnalyticsData, { cache: false });
})
import { getResultFromIdleTask } from 'idle-task';

const checkAccessTokenWhenIdle = (accessToken: string): Promise<any> => {
    const fetchCheckAccessToken = async (): Promise<any> => {
        const response = await fetch(`https://yourdomain/api/check?accessToken=${accessToken}`);
        // Promise callback will execute immediately after fetching completely even if the browser is busy.
        // One of the solutions is to run it when next browser's idle time.
        return getResultFromIdleTask(() => response.json());
    };
    return getResultFromIdleTask(fetchCheckAccessToken);
}

const { isSuccess } = await checkAccessTokenWhenIdle('1234');

React

import {useState, useEffect} from 'react';
import {setIdleTask} from 'idle-task';
import {cancelIdleTask, waitForIdleTask} from "./index";

const fetchNewsList = async () => {
  const response = await fetch('https://yourdomain/api/news');
  return response.json();
}

// this is not important UI for the website main content like e-commerce sites.
export default function WebsiteNewsList() {
  const [newsList, setNewsList] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    // fetch news list when the browser is idle and cache it.
    const taskId = setIdleTask(fetchNewsList)
    waitForIdleTask(taskId)
        .then(setNewsList)
        .finally(() => setIsLoading(false));
    return () => {
        // stop to fetch news list and remove the cache when the component re-render.
        cancelIdleTask(taskId)
    };
  }, [])
  
  if (isLoading) {
      return <div>Loading...</div>
  }
  return newsList.map(news => (
      <div id={news.id}>
        {news.publiedDate}
        {news.title}
        {news.description}
      </div>
  ))
}

License

Released under the MIT license.

About

Improve your website performance by executing JavaScript during a browser's idle periods.

License:MIT License


Languages

Language:TypeScript 98.7%Language:JavaScript 1.1%Language:Shell 0.2%