Last active
February 26, 2016 13:24
-
-
Save pmuellr/a53baaa0b218de152eda to your computer and use it in GitHub Desktop.
Call functions that take async callbacks, in a synchronous style, in a generator, using yield.
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
'use strict' | |
module.exports = yieldCallback | |
// Call functions that use async callbacks, in an "synchronous" type style from | |
// within a generator. | |
if (require.main === module) test() | |
// example invoking a yielding callback generator | |
function test() { | |
yieldCallback(testGen, [__filename], function testCB ($) { | |
if ($.err) return console.log(`error reading ${__filename}: ${$.err}`) | |
console.log(`${__filename} length: ${$.content.length}`) | |
}) | |
} | |
// our yielding callback generator | |
function * testGen (fileName, cbProps) { | |
const fs = require('fs') | |
let $ | |
yield wait(cbProps()) | |
console.log('opening file') | |
$ = yield fs.open(fileName, 'r', cbProps('err fd')) | |
if ($.err) return $ | |
const fd = $.fd | |
yield wait(cbProps()) | |
console.log('getting file size') | |
$ = yield fs.fstat(fd, cbProps('err stats')) | |
if ($.err) { yield fs.close(fd, cbProps()); return $ } | |
if (!$.stats.isFile()) return { err: new Error('not a file') } | |
const buff = new Buffer($.stats.size) | |
yield wait(cbProps()) | |
console.log('reading file') | |
$ = yield fs.read(fd, buff, 0, buff.length, 0, cbProps('err bytesRead buffer')) | |
if ($.err) { yield fs.close(fd, cbProps()); return $ } | |
if ($.bytesRead !== buff.length) return { err: new Error('unexpected file size') } | |
const content = $.buffer.slice(0, $.bytesRead).toString('utf8') | |
yield wait(cbProps()) | |
console.log('closing file') | |
$ = yield fs.close(fd, cbProps('err')) | |
if ($.err) return $ | |
yield wait(cbProps()) | |
console.log('returning file contents') | |
return { content: content } | |
} | |
function wait (cb) { | |
console.log('waiting a half second') | |
setTimeout(cb, 500) | |
} | |
// call with (generatorFn, [arg1, arg2], cb) | |
function yieldCallback (gen, genArgs, cb) { | |
// add cbProps to the argument list | |
genArgs.push(cbProps) | |
// call the generator | |
const iter = gen.apply(null, genArgs) | |
// proceed to first yield | |
iter.next() | |
// code in generatorFn calls this with names of callback arguments | |
function cbProps (vars) { | |
if (!vars) vars = '' | |
// split into individual "variable" names | |
vars = vars.trim().split(/\s+/) | |
// this function will be the fn passed as a callback | |
return function cbParmSetter () { | |
// create the object returned from the yield | |
const object = {} | |
// set the callback arguments as properties of the object | |
for (let i = 0; i < vars.length; i++) { | |
object[vars[i]] = arguments[i] | |
} | |
// return the object from yield, continue to next yield | |
const next = iter.next(object) | |
// if we're done, call the callback with the value returned | |
if (next.done) cb(next.value) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
yieldCallback(generator, generatorArguments, cb)
Call the specified generator function, with the specified arguments. When the generator returns,
cb()
will be invoked with the result.The generator function is called with the arguments you supply, and an additional one at the end,
cbProps
- a function.In place of the usual callback parameter that you'd use with an function that takes an async callback function, use
cbProps('err data')
, which will cause the function invocation via yield to return an object{err: something, data: something}
. The argument tocbProps()
is a space-separated list of property names to set in the returned object. Those properties will be filled in from whatever the real callback is called with.Whatever the generator returns, will be passed to the callback passed to the
yieldCallback()
function.In this example, I use
$
as my running result fromfs
functions. As needed, copy those into their own variables, eg,fd
.