❤Nitrate Processed Mincemeat

    idle-task
    TypeScript icon, indicating that this package has built-in type declarations

    2.13.0 • Public • Published

    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

    Features

    idle-task wraps requestIdleCallback .

    The features are as follows.

    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 as default if the task can't be finished within 1000 ms.

    timeoutStrategy?: 'error' | ’forceRun'

    const generateRandomNumber = () => Math.floor( Math.random() * 100 );
    const taskId = setIdleTask(generateRandomNumber);
    const firstRandomNumber = await waitForIdleTask(taskId, { timeout: 1000, timeoutStrategy: 'forceRun' });

    You can choose the movement when the idle task is timeout.

    waitForIdleTask throws an error as default if the task can't be finished within the time which you set.

    If you set forceRun, idle-task executes the task even if having not yet run it after the time has come.

    getResultFromIdleTask

    const generateRandomNumber = () => Math.floor( Math.random() * 100 );
    const randomNumber = await getResultFromIdleTask(generateRandomNumber, {
        priority: 'high',
        timeout: 3000,
        timeoutStrategy: 'forceRun'
    });
    
    // same
    const taskId = setIdleTask(generateRandomNumber, { priority: 'high' });
    const randomNumber = await waitForIdleTask(taskId, { timeout: 3000, cache: false, timeoutStrategy: 'forceRun' });

    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 .

    forceRunIdleTask

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

    You can get the result immediately whether the task was executed during a browser's idle periods or not.

    forceRunIdleTask can also be set options as below.

    cache?: false

    This is the same as WaitForIdleTaskOptions.cache .

    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: process.env.NODE_ENV === 'development',
      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 false .

    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 });

    timeoutStrategy?: 'error' | ’forceRun'

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

    configureIdleTask({ timeout: 3000, timeoutStrategy: 'forceRun' });
    
    const taskId = setIdleTask(yourFunction);
    // run task in 3000 ms regardless of whether the task has already been executed or not.
    const result = await waitForIdleTask(taskId);
    
    // timeoutStrategy is 'error' if you set timeoutStrategy as option
    try {
      const result = await waitForIdleTask(taskId, { timeoutStrategy: 'error' });  
    } catch {
      console.error('timeout!')
    }

    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

    dynamic import

    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 () => {
        // You should use waitForIdleTask if the module is not important.
        // On the other hand, I recommend to use forceRunIdleTask if the module is important. 
        const { default: sendAnalyticsData } = await waitForIdleTask(taskId);
        // Send analytics data to server when the browser is idle.
        setIdleTask(sendAnalyticsData, { cache: false });
    })

    fetch external resources

    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

    fetch external resources

    import {useState, useEffect} from 'react';
    import {setIdleTask, cancelIdleTask, waitForIdleTask} from 'idle-task';
    
    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>
      ))
    }

    React.lazy

    import {useState, useEffect, lazy, Suspense} from 'react';
    import {setIdleTask, waitForIdleTask, forceRunIdleTask} from 'idle-task';
    
    const taskId = setIdleTask(() => import('~/components/Modal'))
    const taskPromise = waitForIdleTask(taskId)
    const Modal = lazy(() => taskPromise);
    
    export default function WebsiteNewsList() {
      const [isClicked, setIsClicked] = useState(false);
      const onClick = () => setIsClicked(true);
    
      useEffect(() => {
        if (isClicked) {
          // Import Modal immediately whether importing it was completed during the browser's idle periods or not.
          forceRunIdleTask(taskId);
        }
      }, [isClicked])
    
      return (
          <>
            <button type='button' onClick={onClick} />
            <Suspense>
              {isClicked && <Modal />}
            </Suspense>
          </>
      )
    }

    Contributing

    Please see CONTRIBUTING.md .

    License

    Released under the MIT license.

    Install

    npm i idle-task

    DownloadsWeekly Downloads

    192

    Version

    2.13.0

    License

    MIT

    Unpacked Size

    43.2 kB

    Total Files

    7

    Last publish

    Collaborators

    • hiroki0525