{"id":13998509,"url":"https://github.com/hiroki0525/idle-task","last_synced_at":"2025-07-23T06:31:56.941Z","repository":{"id":63124469,"uuid":"562456359","full_name":"hiroki0525/idle-task","owner":"hiroki0525","description":"Improve your website performance by executing JavaScript during a browser's idle periods.","archived":false,"fork":false,"pushed_at":"2024-11-25T20:43:54.000Z","size":2053,"stargazers_count":148,"open_issues_count":2,"forks_count":6,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-11-25T21:33:13.791Z","etag":null,"topics":["cache","javascript","performance","requestidlecallback","typescript","web-performance"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hiroki0525.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"hiroki0525"}},"created_at":"2022-11-06T12:26:30.000Z","updated_at":"2024-11-25T18:43:04.000Z","dependencies_parsed_at":"2024-04-15T21:29:20.263Z","dependency_job_id":"2be14769-8419-4fe7-b78c-b20e768e8366","html_url":"https://github.com/hiroki0525/idle-task","commit_stats":{"total_commits":185,"total_committers":8,"mean_commits":23.125,"dds":0.3675675675675676,"last_synced_commit":"6b589a45dea6e0c03342a70a1ac99932707537c5"},"previous_names":[],"tags_count":40,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hiroki0525%2Fidle-task","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hiroki0525%2Fidle-task/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hiroki0525%2Fidle-task/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hiroki0525%2Fidle-task/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hiroki0525","download_url":"https://codeload.github.com/hiroki0525/idle-task/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227245182,"owners_count":17753239,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","javascript","performance","requestidlecallback","typescript","web-performance"],"created_at":"2024-08-09T19:01:43.993Z","updated_at":"2024-11-30T00:31:40.512Z","avatar_url":"https://github.com/hiroki0525.png","language":"TypeScript","readme":"# idle-task\n\n![idle-task](https://user-images.githubusercontent.com/40714517/202905619-b2319b98-d81a-4cc2-9eac-c88702daf45b.png)\n\n[![npm version](https://badge.fury.io/js/idle-task.svg)](https://badge.fury.io/js/idle-task)\n[![npm bundle size](https://img.shields.io/bundlephobia/minzip/idle-task)](https://bundlephobia.com/package/idle-task)\n[![Test](https://github.com/hiroki0525/idle-task/actions/workflows/test.yml/badge.svg)](https://github.com/hiroki0525/idle-task/actions/workflows/test.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![semantic-release: angular](https://img.shields.io/badge/semantic--release-commitlint_conventional-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release)\n\nImprove your website performance by executing JavaScript during a browser's idle periods.\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n**Table of Contents**\n\n- [Features](#features)\n  - [Manage tasks priority](#manage-tasks-priority)\n  - [Get result by using Promise based API](#get-result-by-using-promise-based-api)\n  - [Cache](#cache)\n  - [Optimize executing tasks](#optimize-executing-tasks)\n  - [Analyze tasks execution time](#analyze-tasks-execution-time)\n- [Install](#install)\n- [Quick Start](#quick-start)\n- [API](#api)\n  - [`setIdleTask`](#setidletask)\n    - [`priority?: 'low' | 'high'`](#priority-low--high)\n    - [`revalidateInterval?: number`](#revalidateinterval-number)\n    - [`revalidateWhenExecuted?: boolean`](#revalidatewhenexecuted-boolean)\n    - [`overwriteTask?: IdleTaskKey`](#overwritetask-idletaskkey)\n  - [`waitForIdleTask`](#waitforidletask)\n    - [`timeout?: number`](#timeout-number)\n    - [`timeoutStrategy?: 'error' | ’forceRun'`](#timeoutstrategy-error--forcerun)\n  - [`getResultFromIdleTask`](#getresultfromidletask)\n  - [`forceRunIdleTask`](#forcerunidletask)\n  - [`cancelIdleTask`](#cancelidletask)\n  - [`cancelAllIdleTasks`](#cancelallidletasks)\n  - [`getIdleTaskStatus`](#getidletaskstatus)\n  - [`configureIdleTask`](#configureidletask)\n    - [`interval?: number`](#interval-number)\n    - [`debug?: boolean`](#debug-boolean)\n    - [`timeout?: number`](#timeout-number-1)\n    - [`timeoutStrategy?: 'error' | ’forceRun'`](#timeoutstrategy-error--forcerun-1)\n- [Recipes](#recipes)\n  - [Vanilla JS](#vanilla-js)\n    - [dynamic import](#dynamic-import)\n    - [fetch external resources](#fetch-external-resources)\n  - [React](#react)\n    - [fetch external resources](#fetch-external-resources-1)\n    - [React.lazy](#reactlazy)\n- [Contributing](#contributing)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Features\n\n`idle-task` wraps [requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback) .\n\nThe features are as follows.\n\n### Manage tasks priority\n\n```javascript\nimport { setIdleTask } from 'idle-task';\nsetIdleTask(yourLowPrioryFunction, { priority: 'low' });\nsetIdleTask(yourHighPrioryFunction, { priority: 'high' });\n```\n\n### Get result by using Promise based API\n\n```javascript\nimport { getResultFromIdleTask } from 'idle-task';\n// get result asynchronously\nconst result = await getResultFromIdleTask(yourFunction);\n```\n\n### Cache\n\n```javascript\nimport { setIdleTask, waitForIdleTask } from 'idle-task';\nconst taskKey = setIdleTask(yourFunction);\nconst result1 = await waitForIdleTask(taskKey);\n// from cache\nconst result2 = await waitForIdleTask(taskKey);\n```\n\n### Optimize executing tasks\n\n```javascript\nimport { setIdleTask } from 'idle-task';\nsetIdleTask(longTask);\n// these functions will be executed during next browser's idle time.\nsetIdleTask(shortTask);\nsetIdleTask(shortTask);\n```\n\n### Analyze tasks execution time\n\n```javascript\nimport { setIdleTask, configureIdleTask } from 'idle-task';\nconfigureIdleTask({ debug: true })\n// output the execution time to the web console.\nsetIdleTask(yourFunction1);\n```\n\n## Install\n\n```shell\nnpm i idle-task\n```\n\n## Quick Start\n\nThe simplest way is to use `setIdleTask` .\n\n```javascript\nimport { setIdleTask } from 'idle-task';\n\nconst sendAnalyticsData = () =\u003e\n        console.log(\"send analytics data during a browser's idle periods.\");\nsetIdleTask(sendAnalyticsData);\n```\n\nIf you want to get the result of a task, please use `waitForIdleTask` .\n\n```javascript\nimport { setIdleTask, waitForIdleTask } from 'idle-task';\n\nconst taskKey = setIdleTask(yourFunction);\nconst result = await waitForIdleTask(taskKey);\n```\n\n## API\n\n### `setIdleTask`\n\n```javascript\nconst sendAnalyticsData = () =\u003e console.log(\"send analytics data\");\nconst options = {\n    priority: 'high',\n    revalidateInterval: 5000,\n    revalidateWhenExecuted: true,\n};\nconst taskKey = setIdleTask(sendAnalyticsData, options);\n```\n\n`idle-task` has a FIFO(First-In-First-Out) queue.\n\n`setIdleTask` enqueues a task which `idle-task` will dequeue and run when the browser is idle.\n\n`setIdleTask` returns `TaskKey` Object which is necessary for `cancelIdleTask` , `getIdleTaskStatus` and `waitForIdleTask`.\n\nI recommend less than **50 ms** to execute a task because of [RAIL model](https://web.dev/i18n/en/rail/) .\nIf you want to know how long did it take to finish a task, please use [debug mode](#debug-boolean) .\n\n`setIdleTask` can also be set options as below.\n\n#### `priority?: 'low' | 'high'`\n\nYou can run a task preferentially using `priority: 'high'` (default is `low`) option.\n`setIdleTask` adds it to the head of the queue.\n\n#### `revalidateInterval?: number`\n\nYou can reregister your task by using `revalidateInterval` .\n\nIf you set `revalidateInterval: 5000` , `idle-task` will enqueue your task every 5000 ms .\n\n```typescript\nconst saveUserArticleDraft = () =\u003e {\n    // save user editing article data to database.\n}\n\n// saveUserArticleDraft will be executed when the browser is idle.\n// In addition, idle-task registers saveUserArticleDraft task every 5000 ms.\nsetIdleTask(saveUserArticleDraft, { revalidateInterval: 5000 });\n```\n\n#### `revalidateWhenExecuted?: boolean`\n\nYou can reregister your task by using `revalidateWhenExecuted` which default is `false`.\n\n`idle-task` will enqueue your task when it had been executed.\n\n```typescript\nconst saveUserArticleDraft = () =\u003e {\n    // save user editing article data to database.\n}\n\n// saveUserArticleDraft will be executed when the browser is idle.\n// In addition, idle-task registers saveUserArticleDraft task when it had been executed.\nsetIdleTask(saveUserArticleDraft, { revalidateWhenExecuted: true });\n```\n\n#### `overwriteTask?: IdleTaskKey`\n\nYou can overwrite registered task by using `overwriteTask`.\n\nIf the task have already been executed, `idle-task` remove its result from the cache and enqueue the new task, otherwise `idle-task` will remove it from the queue and enqueue the new task.\n\n```typescript\nconst generateRandomNumber = () =\u003e Math.floor( Math.random() * 100 );\n\nconst taskKey = setIdleTask(generateRandomNumber);\nconst randomNumber1 = await waitForIdleTask(taskKey);\n\nsetIdleTask(generateRandomNumber, { overwriteTask: taskKey });\nconst randomNumber2 = await waitForIdleTask(taskKey);\n```\n\n### `waitForIdleTask`\n\n```javascript\nconst generateRandomNumber = () =\u003e Math.floor( Math.random() * 100 );\nconst taskKey = setIdleTask(generateRandomNumber);\nconst randomNumber = await waitForIdleTask(taskKey);\n```\n\nYou can get the result of the task by using `waitForIdleTask` .\n\n`waitForIdleTask` can also be set options as below.\n\n#### `timeout?: number`\n\n`waitForIdleTask` maybe wait for the task eternally because it will be finished when the browser is idle.\n`timeout` option can prevent it.\n\n```javascript\nconst generateRandomNumber = () =\u003e Math.floor( Math.random() * 100 );\nconst taskKey = setIdleTask(generateRandomNumber);\ntry {\n    const firstRandomNumber = await waitForIdleTask(taskKey, { timeout: 1000 });\n} catch (e) {\n    if (e instanceof WaitForIdleTaskTimeoutError) {\n        console.error('this is timeout error')\n    }\n}\n```\n\nIn this case, `waitForIdleTask` will throw `WaitForIdleTaskTimeoutError` as default if the task can't be finished within 1000 ms.\n\n#### `timeoutStrategy?: 'error' | ’forceRun'`\n\n```javascript\nconst generateRandomNumber = () =\u003e Math.floor( Math.random() * 100 );\nconst taskKey = setIdleTask(generateRandomNumber);\nconst firstRandomNumber = await waitForIdleTask(taskKey, { timeout: 1000, timeoutStrategy: 'error' });\n```\n\nYou can choose the movement when the idle task is timeout.\n\n`waitForIdleTask` executes the task even if having not yet run it after the time has come.\n\nIf you set `error`, `waitForIdleTask` throws an error if the task can't be finished within the time which you set.\n\n### `getResultFromIdleTask`\n\n```javascript\nconst generateRandomNumber = () =\u003e Math.floor( Math.random() * 100 );\nconst randomNumber = await getResultFromIdleTask(generateRandomNumber, {\n    priority: 'high',\n    timeout: 3000,\n    timeoutStrategy: 'error'\n});\n\n// same\nconst taskKey = setIdleTask(generateRandomNumber, { priority: 'high' });\nconst randomNumber = await waitForIdleTask(taskKey, { timeout: 3000, timeoutStrategy: 'error' });\n```\n\nYou can get the result by using `getResultFromIdleTask` if you don't need the task id.\n\n`getResultFromIdleTask` can also be set options which is `SetIdleTaskOptions.priority` and  `WaitForIdleTaskOptions.timeout` .\n\n### `forceRunIdleTask`\n\n```javascript\nconst generateRandomNumber = () =\u003e Math.floor( Math.random() * 100 );\nconst taskKey = setIdleTask(generateRandomNumber);\nconst randomNumber = await forceRunIdleTask(taskKey);\n```\n\nYou can get the result immediately whether the task was executed during a browser's idle periods or not.\n\n`forceRunIdleTask` gets result from cache if the task was executed.\n\n### `cancelIdleTask`\n\n```javascript\nconst taskKey = setIdleTask(() =\u003e console.log(\"task will be canceled.\"));\ncancelIdleTask(taskKey);\n```\n\nYou can stop to run a task by using `cancelIdleTask` if it is not executed.\n\n### `cancelAllIdleTasks`\n\n```javascript\nsetIdleTask(() =\u003e console.log(\"task 1 will be canceled.\"));\nsetIdleTask(() =\u003e console.log(\"task 2 will be canceled.\"));\nsetIdleTask(() =\u003e console.log(\"task 3 will be canceled.\"));\ncancelAllIdleTasks();\n```\n\nYou can stop to run all tasks by using `cancelAllIdleTasks` if they are not executed.\n\n### `getIdleTaskStatus`\n\n```javascript\nconst taskKey = setIdleTask(() =\u003e console.log(\"task\"));\nconst idleTaskStatus = getIdleTaskStatus(taskKey);\n// execute immediately if the task has not been executed yet.\nif (idleTaskStatus === 'ready') {\n  forceRunIdleTask(taskKey)\n}\n```\n\nYou can know the task status by using `getIdleTaskStatus` .\n\n`getIdleTaskStatus` returns string as following.\n\n- `ready`\n  - The task has not been executed.\n- `executed`\n  - The task has been executed.\n  - **This doesn't mean that the task has been completed** because JavaScript don't have API which help us to know the promise result like `fullfilled` .\n- `unknown`\n  - `idle-task` doesn't know the task status because its result doesn't exist anywhere.\n  - This case means that the task was canceled by API like `cancelIdleTask` .\n\n### `configureIdleTask`\n\n```javascript\nconfigureIdleTask({\n  interval: 1000, // ms\n  debug: process.env.NODE_ENV === 'development',\n  timeout: 3000,\n});\n```\n\n`configureIdleTask` configures `idle-task` .\nYou can set properties as below.\n\n#### `interval?: number`\n\n`idle-task` checks tasks which was registered by `setIdleTask` during a browser's idle periods, so **they will not always be executed** . \n\nPlease set `interval` if you want to guarantee to run tasks as much as possible.\n\nEven 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**.\n\n#### `debug?: boolean`\n\nIf `debug` is `true`, you can know how long did it take to finish the task via the web console.\n\nI recommend less than **50 ms** to execute a task because of [RAIL model](https://web.dev/i18n/en/rail/) .\n\nThe default is `false` .\n\n#### `timeout?: number`\n\nThis option configures `timeout` of `waitForIdleTask` and `getResultFromIdleTask` as **default** setting.\n\n```javascript\nconfigureIdleTask({ timeout: 3000 });\n\nconst taskKey = setIdleTask(yourFunction);\n// timeout is 3000\nconst result = await waitForIdleTask(taskKey);\n\n// timeout is 5000 if you set timeout as option\nconst result = await waitForIdleTask(taskKey, { timeout: 5000 });\n```\n\n#### `timeoutStrategy?: 'error' | ’forceRun'`\n\nThis option configures `timeoutStrategy` of `waitForIdleTask` and `getResultFromIdleTask` as **default** setting.\n\n```javascript\nconfigureIdleTask({ timeout: 3000, timeoutStrategy: 'forceRun' });\n\nconst taskKey = setIdleTask(yourFunction);\n// run task in 3000 ms regardless of whether the task has already been executed or not.\nconst result = await waitForIdleTask(taskKey);\n\n// timeoutStrategy is 'error' if you set timeoutStrategy as option\ntry {\n  const result = await waitForIdleTask(taskKey, { timeoutStrategy: 'error' });  \n} catch {\n  console.error('timeout!')\n}\n```\n\n## Recipes\n\n### Vanilla JS\n\n#### dynamic import\n\n```javascript\nimport { setIdleTask } from 'idle-task';\n\n// this module is loaded during a browser's idle periods because it is not important for UI.\nconst taskKey = setIdleTask(() =\u003e import('./sendAnalyticsData'))\n\nconst button = document.getElementById('button');\nbutton.addEventListener('click', async () =\u003e {\n    // You should use waitForIdleTask if the module is not important.\n    // On the other hand, I recommend to use forceRunIdleTask if the module is important. \n    const { default: sendAnalyticsData } = await waitForIdleTask(taskKey);\n    // Send analytics data to server when the browser is idle.\n    setIdleTask(sendAnalyticsData);\n})\n```\n\n#### fetch external resources\n\n```typescript\nimport { getResultFromIdleTask } from 'idle-task';\n\nconst checkAccessTokenWhenIdle = (accessToken: string): Promise\u003cany\u003e =\u003e {\n    const fetchCheckAccessToken = async (): Promise\u003cany\u003e =\u003e {\n        const response = await fetch(`https://yourdomain/api/check?accessToken=${accessToken}`);\n        // Promise callback will execute immediately after fetching completely even if the browser is busy.\n        // One of the solutions is to run it when next browser's idle time.\n        return getResultFromIdleTask(() =\u003e response.json());\n    };\n    return getResultFromIdleTask(fetchCheckAccessToken);\n}\n\nconst { isSuccess } = await checkAccessTokenWhenIdle('1234');\n```\n\n### React\n\n#### fetch external resources\n\n```jsx\nimport {useState, useEffect} from 'react';\nimport {setIdleTask, cancelIdleTask, waitForIdleTask} from 'idle-task';\n\nconst fetchNewsList = async () =\u003e {\n  const response = await fetch('https://yourdomain/api/news');\n  return response.json();\n}\n\n// this is not important UI for the website main content like e-commerce sites.\nexport default function WebsiteNewsList() {\n  const [newsList, setNewsList] = useState([]);\n  const [isLoading, setIsLoading] = useState(true);\n\n  useEffect(() =\u003e {\n    // fetch news list when the browser is idle and cache it.\n    const taskKey = setIdleTask(fetchNewsList)\n    waitForIdleTask(taskKey)\n        .then(setNewsList)\n        .finally(() =\u003e setIsLoading(false));\n    return () =\u003e {\n        // stop to fetch news list and remove the cache when the component re-render.\n        cancelIdleTask(taskKey)\n    };\n  }, [])\n  \n  if (isLoading) {\n      return \u003cdiv\u003eLoading...\u003c/div\u003e\n  }\n  return newsList.map(news =\u003e (\n      \u003cdiv id={news.id}\u003e\n        {news.publiedDate}\n        {news.title}\n        {news.description}\n      \u003c/div\u003e\n  ))\n}\n```\n\n#### React.lazy\n\n```jsx\nimport {useState, useEffect, lazy, Suspense} from 'react';\nimport {setIdleTask, waitForIdleTask, forceRunIdleTask} from 'idle-task';\n\nconst taskKey = setIdleTask(() =\u003e import('~/components/Modal'))\nconst taskPromise = waitForIdleTask(taskKey)\nconst Modal = lazy(() =\u003e taskPromise);\n\nexport default function WebsiteNewsList() {\n  const [isClicked, setIsClicked] = useState(false);\n  const onClick = () =\u003e setIsClicked(true);\n\n  useEffect(() =\u003e {\n    if (isClicked) {\n      // Import Modal immediately whether importing it was completed during the browser's idle periods or not.\n      forceRunIdleTask(taskKey);\n    }\n  }, [isClicked])\n\n  return (\n      \u003c\u003e\n        \u003cbutton type='button' onClick={onClick} /\u003e\n        \u003cSuspense\u003e\n          {isClicked \u0026\u0026 \u003cModal /\u003e}\n        \u003c/Suspense\u003e\n      \u003c/\u003e\n  )\n}\n```\n\n## Contributing\n\nPlease see [CONTRIBUTING.md](https://github.com/hiroki0525/idle-task/blob/main/CONTRIBUTING.md) .\n\nThank you for contributing!!\n\n[@joeinnes](https://github.com/joeinnes)\n[@m5r](https://github.com/m5r)\n[@yuchi](https://github.com/yuchi)\n\n## License\n\nReleased under the MIT license.\n","funding_links":["https://github.com/sponsors/hiroki0525"],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhiroki0525%2Fidle-task","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhiroki0525%2Fidle-task","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhiroki0525%2Fidle-task/lists"}