commit 9194eeb709976f790cf1dcbcffac916da056b815 Author: Timothy Farrell Date: Fri Jul 20 00:49:20 2018 -0500 Split out backgroundtask in prep for attachmentProxy separation. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4ab2b3d --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# Background task + +An simple function wrapper around +[requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback) +that queues tasks and runs them sequentially. + +The main purpose is to be able to run maintainance functions that are not a good fit for a web +worker context. + +# Usage + +Wrap your function. + +``` + const task = backgroundTask(() => { + // do stuff + }); +``` + +Call `task` as you would any other function that has no return value. The return value of a +background task is the id from `requestIdleCallback` that can be passed to `cancelIdleCallback` if +you wish to cancel the task before it runs. + +Calling a task multiple times will queue the passed parameters and execute the task sequentially the +same number of times. diff --git a/package.json b/package.json new file mode 100644 index 0000000..83c1e04 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "backgroundtask", + "version": "1.0.0", + "description": "A simple way to run a task on the main thread without disrupting the UX", + "main": "src/index.js", + "files": [ + "src" + ], + "scripts": { + "test": "node ../../bin/runTests.js ./", + "pre-commit": "npm run test" + }, + "author": "Timothy Farrell (https://github.com/explorigin)", + "license": "Apache-2.0" +} diff --git a/spec/backgroundTask.spec.js b/spec/backgroundTask.spec.js new file mode 100644 index 0000000..0185962 --- /dev/null +++ b/spec/backgroundTask.spec.js @@ -0,0 +1,46 @@ +import { backgroundTask } from '../src/index.js'; + +describe('A background task', () => { + it('initially does nothing.', done => { + const task = backgroundTask(() => + done(new Error('Background tasks should not spontaneously execute.')) + ); + expect().nothing(); + setTimeout(done, 50); + }); + + it('runs a task (but not right away)', done => { + let shouldRun = false; + const task = backgroundTask(() => { + expect(shouldRun).toBeTruthy(); + done(); + }); + + task(); + shouldRun = true; + setTimeout(done, 50); + }); + + it('passes params', done => { + let testVal = Math.random(); + const task = backgroundTask(val => { + expect(val).toEqual(testVal); + done(); + }); + + task(testVal); + setTimeout(done, 50); + }); + + it('queues multiple calls to be run sequentially', done => { + let testVal = ['a', 'd', 'q']; + const task = backgroundTask((val, index) => { + expect(val).toEqual(testVal[index]); + if (index === testVal.length - 1) { + done(); + } + }); + + testVal.map(task); + }); +}); diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..6055ecb --- /dev/null +++ b/src/index.js @@ -0,0 +1,46 @@ +// requestIdleCallback sortof-polyfill +if (!self.requestIdleCallback) { + const IDLE_TIMEOUT = 10; + self.requestIdleCallback = cb => { + let start = Date.now(); + return setTimeout( + () => + cb({ + timeRemaining: () => Math.max(0, IDLE_TIMEOUT - (Date.now() - start)) + }), + 1 + ); + }; + self.requestIdleCallback = clearTimeout; +} + +export function backgroundTask(fn, timeout = undefined) { + let id = null; + const params = []; + + async function runTask({ didTimeout }) { + if (didTimeout) { + id = requestIdleCallback(runTask); + return; + } + const start = Date.now(); + const p = params.shift(); + await fn(...p); + if (params.length) { + id = requestIdleCallback(runTask, { timeout }); + } else { + id = null; + } + } + + const wrapper = (...args) => { + params.push(args); + if (id !== null) { + return false; + } + id = requestIdleCallback(runTask, { timeout }); + return true; + }; + + return wrapper; +}