control-flow
Version:
Turns asynchronous function into synchronous
87 lines (77 loc) • 2.79 kB
JavaScript
require('fibers')
var flow = module.exports = {}
// Creates special, fiber-aware asynchronous callback resuming current fiber when it will be finished.
flow.promise = function(){
var fiber = Fiber.current
if(!fiber) throw "no current Fiber, promise can'b be used without Fiber!"
return function(){
var thatArguments = arguments
// Wrapping in nextTick as a safe measure against not asynchronous usage.
process.nextTick(function(){
if(thatArguments[0]){
// Resuming fiber and throwing error.
fiber.throwInto(thatArguments[0])
}else{
// Resuming fiber and returning result.
fiber.run(thatArguments[1])
}
})
}
}
// Takes function and returns its synchronized version, it's still backward compatible and
// can be used as asynchronous `syncFn = sync(asyncFn)`.
//
// Or You can provide object and it will synchronize its functions `sync(obj, fname1, fname2, ...)`.
//
// New synchronized version of function is backward compatible and You may call it as usual with
// explicit callback or inside of `fiber` with `yield` keyword.
flow.sync = function(){
if(arguments.length > 1){
// Synchronizing functions of object.
var obj = arguments[0]
for(var i = 1; i < arguments.length; i++){
var fname = arguments[i]
var fn = obj[fname]
if(!fn) throw new Error("object doesn't have '" + fname + "' function!")
obj[fname] = flow.sync(fn)
}
}else{
var fn = arguments[0]
// Preventing function to be synchronized twice.
if(fn._synchronized) return fn
// Synchronized version of function.
var sFn = function(){
// Using fibers only if there's active fiber and callback not provided explicitly.
if(Fiber.current && (typeof arguments[arguments.length-1] !== 'function')){
// Calling asynchronous function with our special fiber-aware callback.
Array.prototype.push.call(arguments, flow.promise())
return fn.apply(this, arguments)
}else{
// If there's no active fiber or callback provided explicitly we call original version.
return fn.apply(this, arguments)
}
}
// Marking function as synchronized.
sFn._synchronized = true
return sFn
}
}
// Executes `callback` within `Fiber`, when it finish it will call `done` callback.
// If error will be thrown during execution, this error will be catched and passed to `done`,
// if `done` not provided it will be just rethrown.
flow.fiber = function(callback, done){
var that = this
Fiber(function(){
if (done) {
try {
callback.call(that)
done()
} catch (error){
done(error)
}
} else {
// Don't catch errors if done not provided!
callback.call(that)
}
}).run()
}