Skip to content

Instantly share code, notes, and snippets.

@kjin
Last active March 22, 2018 00:31
Show Gist options
  • Save kjin/96496380a75b92b326f747c7846fa3cf to your computer and use it in GitHub Desktop.
Save kjin/96496380a75b92b326f747c7846fa3cf to your computer and use it in GitHub Desktop.
import * as assert from 'assert';
import { AsyncResource } from "async_hooks";
import * as express from 'express';
type AsyncTaskCallback<T> = (err: Error|null, output?: T) => void;
type AsyncTask<I, O> = (input: I, cb: AsyncTaskCallback<O>) => void;
/** USERSPACE QUEUEING LIBRARY */
// Takes an arbitrary function that represents an async task with an array of inputs and
// outputs, and returns a function that will take a single input/output cb and queue it for this
// task.
function makeSingular<I, O>(task: AsyncTask<I[], O[]>, queueSize: number): AsyncTask<I, O> {
const queue: Array<{ input: I, cb: AsyncTaskCallback<O>, asyncResource: AsyncResource }> = [];
return (input: I, cb: AsyncTaskCallback<O>) => {
// The AsyncResource here refers to the "asynchronous hop" from the user's perspective
// between when the the singular task was created, and when it was actually started.
// It is needed to preserve context.
const asyncResource = new AsyncResource('SingularizedTask');
queue.push({ input, cb, asyncResource });
if (queue.length === queueSize) {
// Capture the values in the queue as it will be cleared later
const q = queue.map(a => a);
queue.map(e => e.asyncResource.emitBefore());
task(queue.map(e => e.input), (err, outputs) => {
q.forEach((e, index) => {
if (outputs) {
e.cb(null, outputs[index]);
} else {
e.cb(err);
}
});
q.length = 0;
});
// Reverse to preserve perfect nesting.
queue.map(a => a).reverse().map(e => e.asyncResource.emitAfter());
queue.map(e => e.asyncResource.emitDestroy());
queue.length = 0;
// If we do anything here, it will be attributed to the wrong AsyncResource.
}
};
}
/** USING THE LIBRARY */
const sleepSort: AsyncTask<number[], number[]> = (inputs, cb) => {
const outputs: number[] = [];
inputs.forEach(input => setTimeout(() => {
outputs.push(input);
if (outputs.length === inputs.length) {
cb(null, outputs);
}
}, input));
}
// Only runs when there are five elements to be sorted.
const singularSleepSort = makeSingular(sleepSort, 5);
// Example
for (let i = 5; i > 0; i--) {
singularSleepSort(i, (err, output) => { console.log(`${i} -> ${output}`); });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment