1 | Fiber = require 'fibers'
|
2 | Future = require 'fibers/future'
|
3 |
|
4 |
|
5 |
|
6 | functionWithFiberReturningFuture = Function::future
|
7 |
|
8 | module.exports = fibrous = (fn) ->
|
9 | futureFn = functionWithFiberReturningFuture.call(fn)
|
10 |
|
11 |
|
12 |
|
13 | asyncFn = (cb) ->
|
14 | args = if 1 <= arguments.length then Array.prototype.slice.call(arguments, 0) else []
|
15 | callback = args.pop()
|
16 | throw new Error("Fibrous method expects a callback") unless callback instanceof Function
|
17 | future = futureFn.apply(@, args)
|
18 | future.resolve callback
|
19 | Object.defineProperty asyncFn, '__fibrousFn__', value: fn, enumerable: false
|
20 | Object.defineProperty asyncFn, '__fibrousFutureFn__', value: futureFn, enumerable: false
|
21 | asyncFn
|
22 |
|
23 |
|
24 | futureize = (asyncFn) ->
|
25 | (args...) ->
|
26 | fnThis = @ is asyncFn and global or @
|
27 |
|
28 |
|
29 | return asyncFn.__fibrousFutureFn__.apply(fnThis, args) if asyncFn.__fibrousFutureFn__
|
30 |
|
31 | future = new Future
|
32 | args.push(future.resolver())
|
33 | try
|
34 | asyncFn.apply(fnThis, args)
|
35 | catch e
|
36 |
|
37 | future.throw(e)
|
38 | future
|
39 |
|
40 | synchronize = (asyncFn) ->
|
41 | (args...) ->
|
42 |
|
43 | return asyncFn.__fibrousFn__.apply(@ is asyncFn and global or @, args) if asyncFn.__fibrousFn__
|
44 |
|
45 | asyncFn.future.apply(@, args).wait()
|
46 |
|
47 | proxyAll = (src, target, proxyFn) ->
|
48 | for key in Object.keys(src)
|
49 | do (key) ->
|
50 | return if Object::[key]?
|
51 | return if Object.getOwnPropertyDescriptor(src, key).get?
|
52 | return unless typeof src[key] is 'function'
|
53 |
|
54 | target[key] = proxyFn(key)
|
55 |
|
56 | target
|
57 |
|
58 | proxyBuilder = (futureOrSync) ->
|
59 | (that) ->
|
60 | result =
|
61 | if typeof(that) is 'function'
|
62 | func = (futureOrSync is 'future' and futureize or synchronize)(that)
|
63 | func.__proto__ = Object.getPrototypeOf(that)[futureOrSync] if Object.getPrototypeOf(that) isnt Function.prototype
|
64 | func
|
65 | else
|
66 | Object.create(Object.getPrototypeOf(that) and Object.getPrototypeOf(that)[futureOrSync] or Object::)
|
67 |
|
68 | result.that = that
|
69 |
|
70 | proxyAll that, result, (key) ->
|
71 | (args...) ->
|
72 |
|
73 | @that[key][futureOrSync].apply(@that, args)
|
74 |
|
75 |
|
76 | defineMemoizedPerInstanceProperty = (target, propertyName, factory) ->
|
77 | cacheKey = "__fibrous#{propertyName}__"
|
78 | Object.defineProperty target, propertyName,
|
79 | enumerable: false
|
80 | set: (value) ->
|
81 | delete @[cacheKey]
|
82 | Object.defineProperty @, propertyName, value: value, writable:true, configurable: true, enumerable: true
|
83 | get: ->
|
84 | unless @hasOwnProperty(cacheKey) and @[cacheKey]
|
85 | Object.defineProperty @, cacheKey, value: factory(@), writable: true, configurable: true, enumerable: false
|
86 | @[cacheKey]
|
87 |
|
88 |
|
89 | for base in [Object::, Function::]
|
90 | defineMemoizedPerInstanceProperty(base, 'future', proxyBuilder('future'))
|
91 | defineMemoizedPerInstanceProperty(base, 'sync', proxyBuilder('sync'))
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 | fibrous.wait = (futures...) ->
|
99 | getResults = (futureOrArray) ->
|
100 | return futureOrArray.get() if (futureOrArray instanceof Future)
|
101 | getResults(i) for i in futureOrArray
|
102 |
|
103 | Future.wait(futures...)
|
104 | result = getResults(futures)
|
105 | result = result[0] if result.length == 1
|
106 | result
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 | fibrous.middleware = (req, res, next) ->
|
119 | process.nextTick ->
|
120 | Fiber ->
|
121 | try
|
122 | next()
|
123 | catch e
|
124 |
|
125 | console.error('Unexpected error bubble up to the top of the fiber:', e?.stack or e)
|
126 | .run()
|