Skip to content

Instantly share code, notes, and snippets.

@zerobias
Created September 10, 2018 07:22
Show Gist options
  • Select an option

  • Save zerobias/ac6ee5992c1f5295f6b34ec119d4cdc0 to your computer and use it in GitHub Desktop.

Select an option

Save zerobias/ac6ee5992c1f5295f6b34ec119d4cdc0 to your computer and use it in GitHub Desktop.
Async worker
import * as promises from './b_promises.js';
const allowedWorkTime = 4;
const maximumTaskFrame = 100;
export default class Worker {
constructor(fn) {
this.fn_ = fn;
this.queue_ = [];
this.waiting_ = null;
this.runner_().catch((err) => {
console.info('worker runner failed', err);
throw err;
});
}
async runner_() {
await new Promise((resolve) => this.waiting_ = resolve);
this.waiting_ = null;
await promises.idle();
for (;;) {
if (this.chunk_()) {
return this.runner_();
}
await promises.rAF();
}
}
/**
* Completes a chunk of work.
*
* @return {boolean} Whether work is done.
*/
chunk_() {
const start = window.performance.now();
let done = 0;
while (this.queue_.length) {
const next = this.queue_.shift();
next.resolve(this.fn_(next.arg));
++done;
if (done == maximumTaskFrame || window.performance.now() - start > allowedWorkTime) {
break;
}
}
return !this.queue_.length;
}
task(arg) {
return new Promise((resolve) => {
this.queue_.push({resolve, arg});
this.waiting_ && this.waiting_();
});
}
}
const resolved = Promise.resolve();
/**
* Returns a Promise that resolves on `requestIdleCallback`.
*
* @return {!Promise<!IdleDeadline>}
*/
export function idle() {
return new Promise((resolve) => window.requestIdleCallback(resolve));
}
/**
* Returns a Promise that resolves on `requestAnimationFrame`.
*
* @param {number=} delay whether to also delay by `setTimeout`
* @return {!Promise<!IdleDeadline>}
*/
export function rAF(delay=undefined) {
if (delay !== undefined) {
return new Promise((resolve) => {
window.setTimeout(() => window.requestAnimationFrame(resolve), delay);
});
}
return new Promise((resolve) => window.requestAnimationFrame(resolve));
}
/**
* Returns a Promise that resolves after a microtask.
*
* @return {!Promise<void>}
*/
export function microtask() {
return Promise.resolve();
}
/**
* @return {{promise: !Promise<void>, resolver: function(): void}}
*/
export function resolver() {
let resolver;
const promise = new Promise((resolve) => resolver = resolve);
return {resolver, promise};
}
/**
* @return {!Promise<void>}
*/
export function delay(ms=0) {
return new Promise((resolve) => window.setTimeout(resolve, ms));
}
const debouceMap = new Map();
/**
* Returns a Promise that debounces a call to the passed callable.
*
* @template T
* @param {function(): T} callable
* @param {number=} delay
* @return {!Promise<T>}
*/
export function debounce(callable, delay=0) {
let state = debouceMap.get(callable);
if (!state) {
state = {c: callable};
const p = new Promise((resolve) => state.r = resolve);
state.p = p.then(() => {
debouceMap.delete(callable);
return state.c();
});
debouceMap.set(callable, state);
}
window.clearTimeout(state.t);
state.t = window.setTimeout(state.r, Math.max(0, delay));
return state.p;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment