Skip to content

Commit

Permalink
Add browser task scheduler.
Browse files Browse the repository at this point in the history
  • Loading branch information
RubenVerborgh committed Jul 6, 2020
1 parent abc6a73 commit aab906f
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 22 deletions.
10 changes: 10 additions & 0 deletions .change-extension
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash

# The first parameter is the desired extension
extension=$1
shift

# The other parameters are the files to be renamed with the extension
for file in "$@"; do
mv "$file" "${file%.*}.$extension"
done
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
no-undef-init: error,
no-undefined: off,
no-unused-vars: [ error, { args: none } ],
"@typescript-eslint/no-unused-vars": [ error, { args: none } ],
no-use-before-define: [ error, { functions: false, classes: false } ],

// Node.js
Expand Down Expand Up @@ -229,7 +230,7 @@
no-confusing-arrow: off,
no-const-assign: error,
no-dupe-class-members: error,
no-duplicate-imports: error,
no-duplicate-imports: off,
no-new-symbol: error,
no-restricted-imports: error,
no-this-before-super: error,
Expand Down
9 changes: 4 additions & 5 deletions asynciterator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
*/

import { EventEmitter } from 'events';
import queueMicrotask from 'queue-microtask';
import createTaskScheduler from './taskscheduler';
import type { Task, TaskScheduler } from './taskscheduler';

let taskScheduler: TaskScheduler = queueMicrotask;
let taskScheduler: TaskScheduler = createTaskScheduler();

/** Schedules the given ask for asynchronous execution. */
export function scheduleTask(task: () => void): void {
export function scheduleTask(task: Task): void {
taskScheduler(task);
}

Expand Down Expand Up @@ -1875,5 +1876,3 @@ type SourceExpression<T> =

type InternalSource<T> =
AsyncIterator<T> & { _destination: AsyncIterator<any> };

type TaskScheduler = (task: () => void) => void;
5 changes: 0 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 9 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@
"module": "./asynciterator.mjs",
"sideEffects": false,
"files": [
"asynciterator.mjs",
"asynciterator.cjs",
"asynciterator.d.ts"
"*.mjs",
"*.cjs",
"*.d.ts"
],
"scripts": {
"build": "npm run build:module && npm run build:commonjs && npm run build:types",
"build:module": "tsc --module es2015 && mv asynciterator.js asynciterator.mjs",
"build:commonjs": "tsc --module commonjs && mv asynciterator.js asynciterator.cjs",
"build:types": "tsc -d && rm asynciterator.js",
"build": "npm run build:clean && npm run build:module && npm run build:commonjs && npm run build:types",
"build:clean": "rm -f *.js *.mjs *.cjs *.d.ts",
"build:module": "tsc --module es2015 && ./.change-extension mjs *.js && npm run build:fix-import",
"build:commonjs": "tsc --module commonjs && ./.change-extension cjs *.js",
"build:types": "tsc -d && rm *.js",
"build:fix-import": "sed -i '' -e 's/taskscheduler/taskscheduler.mjs/' asynciterator.mjs",
"prepare": "npm run build",
"test": "npm run build:module && npm run test:microtask",
"test:microtask": "npm run mocha",
Expand All @@ -33,9 +35,6 @@
},
"license": "MIT",
"repository": "RubenVerborgh/AsyncIterator",
"dependencies": {
"queue-microtask": "^1.1.2"
},
"devDependencies": {
"@babel/cli": "^7.10.1",
"@babel/core": "^7.10.2",
Expand Down
1 change: 0 additions & 1 deletion queue-microtask.d.ts

This file was deleted.

24 changes: 24 additions & 0 deletions taskscheduler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const resolved = Promise.resolve(undefined);

// Returns a function that asynchronously schedules a task
export default function createTaskScheduler() : TaskScheduler {
// Use or create a microtask scheduler
const scheduleMicrotask = typeof queueMicrotask === 'function' ?
queueMicrotask : (task: Task) => resolved.then(task);

// If not in the browser, always use the microtask scheduler
if (typeof window === 'undefined')
return scheduleMicrotask;

// In the browser, alternate with setTimeout to avoid freezing
let i = 0;
return (task: Task) => {
if (++i < 100)
scheduleMicrotask(task);
else
setTimeout(task, i = 0);
}
}

export type Task = () => void;
export type TaskScheduler = (task: Task) => void;
49 changes: 49 additions & 0 deletions test/TaskScheduler-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
setTaskScheduler,
} from '../asynciterator.mjs';

import createTaskScheduler from '../taskscheduler.mjs';

describe('TaskScheduler', () => {
describe('scheduleTask', () => {
it('is a function', () => {
Expand Down Expand Up @@ -48,4 +50,51 @@ describe('TaskScheduler', () => {
setTaskScheduler(scheduler);
});
});

describe('a task scheduler for the browser', () => {
const backups = {};

before(() => {
backups.setTimeout = global.setTimeout;
backups.queueMicrotask = global.queueMicrotask;
global.window = {};
global.setTimeout = sinon.spy();
global.queueMicrotask = sinon.spy();
});

after(() => {
global.setTimeout = backups.setTimeout;
global.queueMicrotask = backups.queueMicrotask;
delete global.window;
});

it('alternates between setTimeout and queueMicrotask', () => {
const taskScheduler = createTaskScheduler();
const task = sinon.spy();
for (let i = 0; i < 100; i++)
taskScheduler(task);
expect(global.setTimeout).to.have.callCount(1);
expect(global.queueMicrotask).to.have.callCount(99);
expect(global.setTimeout).to.have.been.calledWith(task, 0);
expect(global.queueMicrotask).to.have.been.calledWith(task);
});
});

describe('a task scheduler when queueMicrotask is unavailable', () => {
const backups = {};

before(() => {
backups.queueMicrotask = global.queueMicrotask;
delete global.queueMicrotask;
});

after(() => {
global.queueMicrotask = backups.queueMicrotask;
});

it('can schedule a task', done => {
const taskScheduler = createTaskScheduler();
taskScheduler(done);
});
});
});

0 comments on commit aab906f

Please sign in to comment.