Split out backgroundtask in prep for attachmentProxy separation.

This commit is contained in:
Timothy Farrell 2018-07-20 00:49:20 -05:00
parent 5d7d23688d
commit cc3f66daba
9 changed files with 150 additions and 65 deletions

View File

@ -1,18 +1,20 @@
# Portfolio # Portfolio
These are my personal projects. These are my personal projects. This is where I come to refactor to my heart's content.
# Packages # Projects
## [Gallery](./packages/gallery/README.md)
A browser-based app for viewing photos. (WIP)
# Major Tools
## [FRP tools](./packages/frptools/README.md) ## [FRP tools](./packages/frptools/README.md)
Observable property and computed value stores designed to work together for storing real and derived Observable property and computed value stores designed to work together for storing real and derived
state. state.
## [Gallery](./packages/gallery/README.md)
A browser-based app for viewing photos. (WIP)
## [Portal](./packages/portal/README.md) ## [Portal](./packages/portal/README.md)
A utility to expose an asynchronous API between a web worker and its parent. A utility to expose an asynchronous API between a web worker and its parent.
@ -26,6 +28,12 @@ An type-based abstraction layer over PouchDB inspired by [Hood.ie](https://hood.
A slim and unopinionated hash router. A slim and unopinionated hash router.
# Minor Tools
## [Background Task](./packages/backgroundtask/README.md)
A simple way to run a task on the main thread without disrupting the UX (much).
## [Trimkit](./packages/trimkit/README.md) ## [Trimkit](./packages/trimkit/README.md)
Javascript API abstractions to enhance minification by substituting variables. It's really quite Javascript API abstractions to enhance minification by substituting variables. It's really quite

View File

@ -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.

View File

@ -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 <tim@thecookiejar.me> (https://github.com/explorigin)",
"license": "Apache-2.0"
}

View File

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

View File

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

View File

@ -12,6 +12,7 @@
"dev": "webpack-dev-server" "dev": "webpack-dev-server"
}, },
"dependencies": { "dependencies": {
"backgroundtask": "~1.0.0",
"body-parser": "~1.18.3", "body-parser": "~1.18.3",
"date-fns": "~1.29.0", "date-fns": "~1.29.0",
"domvm": "~3.2.1", "domvm": "~3.2.1",

View File

@ -1,8 +1,8 @@
import { TypeHandler } from 'pouchtype'; import { TypeHandler } from 'pouchtype';
import { backgroundTask } from 'backgroundtask';
import { db } from '../services/db.js'; import { db } from '../services/db.js';
import { blobToArrayBuffer, deepAssign } from '../utils/conversion.js'; import { blobToArrayBuffer, deepAssign } from '../utils/conversion.js';
import { backgroundTask } from '../utils/event.js';
import { FileType } from './file.js'; import { FileType } from './file.js';
import { error } from '../utils/console.js'; import { error } from '../utils/console.js';

View File

@ -1,5 +1,6 @@
import core from 'pouchdb-core'; import core from 'pouchdb-core';
import { backgroundTask } from '../utils/event.js'; import { backgroundTask } from 'backgroundtask';
import { deepAssign, blobToObj } from '../utils/conversion.js'; import { deepAssign, blobToObj } from '../utils/conversion.js';
import { error, log } from '../utils/console.js'; import { error, log } from '../utils/console.js';

View File

@ -1,62 +1,5 @@
import { log, group, groupEnd } from '../utils/console.js'; import { log, group, groupEnd } from '../utils/console.js';
// requestIdleCallback sortof-polyfill
if (!global.requestIdleCallback) {
const IDLE_TIMEOUT = 10;
global.requestIdleCallback = cb => {
let start = Date.now();
return setTimeout(
() =>
cb({
timeRemaining: () => Math.max(0, IDLE_TIMEOUT - (Date.now() - start))
}),
1
);
};
}
export function backgroundTask(fn, initialDelay = 500) {
let id = null;
const params = [];
async function runTask({ didTimeout }) {
if (didTimeout) {
id = requestIdleCallback(runTask);
return;
}
const start = Date.now();
group(fn.name);
const p = params.shift();
if (p.length) {
log(`${fn.name} params: `, ...p);
}
await fn(...p);
const executionTime = Date.now() - start;
log(`${fn.name} execution time: ${executionTime}ms`);
groupEnd(fn.name);
if (params.length) {
id = requestIdleCallback(runTask);
} else {
id = null;
}
}
const wrapper = (...args) => {
params.push(args);
if (id !== null) {
return false;
}
id = requestIdleCallback(runTask);
return true;
};
if (initialDelay) {
setTimeout(wrapper, initialDelay);
}
return wrapper;
}
export const streamConfig = { export const streamConfig = {
is: s => s && typeof s.subscribe === 'function', is: s => s && typeof s.subscribe === 'function',
val: s => s(), val: s => s(),