Last active
March 22, 2018 00:31
-
-
Save kjin/96496380a75b92b326f747c7846fa3cf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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