Created
May 29, 2013 06:13
-
-
Save DmitrySoshnikov/5668283 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
"use strict"; | |
const slice = [].slice; | |
/** | |
* A user callback function that is raised when the result of an async function | |
* is available. | |
* | |
* @param Any error | |
* If truthy, indicates an error result. Will be thrown from the | |
* generator. | |
* @param Any result | |
* If successful this result will be the value of the `yield`. | |
*/ | |
function Callback(error, result){} | |
/** | |
* A function that can be takes a callback as its last parameter. | |
* | |
* @params Any args | |
* Any amount of arguments. | |
* @return Continuation | |
* The continuation for the args as applied to this Continuable. | |
*/ | |
function Continuable(/*...args*/){} | |
/** | |
* The type of function that must be yielded from a generator being run. | |
* | |
* @param Callback | |
* The callback which will handle the error or result of the | |
* continuation. | |
*/ | |
function Continuation(callback){} | |
/** | |
* Uses continuation passing style to pump through each yield in a generator. | |
* | |
* @param Generator generator | |
* The generator object to pump. | |
* @param Continuation cont | |
* The continuation which will be passed the callback that dispatches to | |
* the generator. | |
*/ | |
function pump(generator, cont){ | |
cont(function(err, result){ | |
if (err) { | |
generator.throw(err); | |
} else { | |
const next = generator.send(result).value; | |
next && pump(generator, next); | |
} | |
}); | |
} | |
/** | |
* Creates a generator from a generator function and begins executing it. | |
* | |
* @param GeneratorFunction generatorFn | |
* The generator function to execute. Every yielded value should be a | |
* function that works with `pump`, such as those created with `wrap`. | |
*/ | |
exports.run = function run(generatorFn){ | |
const generator = generatorFn(); | |
pump(generator, generator.next().value); | |
}; | |
/** | |
* Wrap an async function as a Continuable function so it can be used with | |
* `run`. | |
* | |
* @param Function fn | |
* Function to wrap. This function must accept a Callback as its last | |
* parameter. | |
* @return Continuable | |
* Wrapped version of the function that can be yielded to in a | |
* generator executed using `run`. | |
*/ | |
exports.makeContinuable = function makeContinuable(fn){ | |
return function(){ | |
const args = slice.call(arguments); | |
const receiver = this; | |
return function(cb){ | |
fn.apply(receiver, args.concat(cb)); | |
}; | |
}; | |
}; | |
/** | |
* Helper that can be used to pause execution. | |
* | |
* @param Number ms | |
* Time to pause. | |
* @return Continuation | |
* Function that can be be yielded to in a generator executed using | |
* `run`. | |
*/ | |
exports.sleep = function sleep(ms){ | |
return function(cb){ | |
const start = Date.now(); | |
setTimeout(function(){ | |
cb(null, Date.now() - start); | |
}, ms); | |
}; | |
}; | |
/** | |
* Helper that waits until the next event loop tick. | |
* | |
* @return Continuation | |
* Function that can be be yielded to in a generator executed using | |
* `run`. | |
*/ | |
exports.tick = function tick(){ | |
return function(cb){ | |
process.nextTick(cb); | |
}; | |
}; | |
/** | |
* Defers execution until the end of the current microtask | |
* | |
* @return Continuation | |
* Function that can be be yielded to in a generator executed using | |
* `run`. | |
*/ | |
exports.defer = function defer(){ | |
return function(fn){ | |
setImmediate(fn); | |
}; | |
}; | |
/** | |
* Maps a Continuable function over a set of values. Meant to be used with | |
* delegating yield (yield*). | |
* | |
* @param Array array | |
* The set of values to map over. | |
* @param Continuable fn | |
* The function to be applied to each value. | |
* @return Array | |
* The mapped array. | |
*/ | |
exports.map = function* map(array, cb, receiver){ | |
const result = []; | |
for (let i = 0; i < array.length; i++) { | |
if (i in array) { | |
result.push(yield cb.call(receiver, array[i], i, array)); | |
} | |
} | |
return result; | |
}; | |
/** | |
* Uses a Continuable function to filter a set of values. Meant to be used with | |
* delegating yield (yield*). | |
* | |
* @param Array array | |
* The set of values to map over. | |
* @param Continuable fn | |
* The function to be applied to each value. | |
* @return Array | |
* The filtered array. | |
*/ | |
exports.filter = function* filter(array, cb, receiver){ | |
const result = []; | |
for (let i = 0; i < array.length; i++) { | |
if (i in array && (yield cb.call(receiver, array[i], i, array))) { | |
result.push(array[i]); | |
} | |
} | |
return result; | |
}; | |
/** | |
* Uses a Continuable function to reduce a set of values. Meant to be used with | |
* delegating yield (yield*). | |
* | |
* @param Array array | |
* The set of values to map over. | |
* @param Continuable fn | |
* The function to be applied to each value. | |
* @return Any | |
*/ | |
exports.reduce = function* reduce(array, cb, initial){ | |
let index, accum; | |
if (arguments.length < 3) { | |
index = 1; | |
accum = array[0]; | |
} else { | |
index = 0; | |
accum = initial; | |
} | |
for (let i = index; i < array.length; i++) { | |
if (i in array) { | |
accum = yield cb(accum, array[i], array); | |
} | |
} | |
return accum; | |
}; | |
/** | |
* Returns true the first time a Continuable function returns a truthy value | |
* against a set of values, otherwise returns false. Meant to be used with | |
* delegating yield (yield*). | |
* | |
* @param Array array | |
* The set of values to map over. | |
* @param Continuable fn | |
* The function to be applied to each value. | |
* @return Boolean | |
*/ | |
exports.some = function* some(array, cb, receiver){ | |
for (let i = 0; i < array.length; i++) { | |
if (i in array && (yield cb.call(receiver, array[i], i, array))) { | |
return true; | |
} | |
} | |
return false; | |
}; | |
/** | |
* Returns false the first time a Continuable function returns a falsey value | |
* against a set of values, otherwise returns true. Meant to be used with | |
* delegating yield (yield*). | |
* | |
* @param Array array | |
* The set of values to map over. | |
* @param Continuable fn | |
* The function to be applied to each value. | |
* @return Boolean | |
*/ | |
exports.every = function* every(array, cb, receiver){ | |
for (let i = 0; i < array.length; i++) { | |
if (i in array && !(yield cb.call(receiver, array[i], i, array))) { | |
return false; | |
} | |
} | |
return true; | |
}; |
This file contains hidden or 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
"use strict"; | |
const fs = require('fs'); | |
const path = require('path'); | |
const gen = require('./continuable-generators'); | |
const readdir = gen.makeContinuable(fs.readdir); | |
const stat = gen.makeContinuable(fs.stat); | |
function* fulldir(dir){ | |
return (yield readdir(dir)).map(function(child){ | |
return path.resolve(dir, child); | |
}); | |
} | |
function* statdir(dir){ | |
const children = yield* fulldir(dir); | |
return yield* gen.map(children, function(item){ | |
return stat(item); | |
}); | |
} | |
function* sizedir(dir){ | |
const children = (yield* statdir(dir)).filter(function(child){ | |
return child.isFile(); | |
}); | |
return children.reduce(function(total, child){ | |
return total + child.size; | |
}, 0); | |
} | |
gen.run(function*(){ | |
console.log(yield* sizedir('.')); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment