1 | var Fiber = require('../fibers');
|
2 | var _ = require('lodash');
|
3 | var Config = require('./config');
|
4 | var FiberMgr = require('./fiberManager');
|
5 | var RunContext = require('./runContext');
|
6 | var Semaphore = require('./semaphore');
|
7 | var AsyncIterator = require('./asyncIterator');
|
8 | var defer = require('./defer');
|
9 | var await = require('../await/index');
|
10 |
|
11 | function makeAsyncFunc(config) {
|
12 |
|
13 | config.validate();
|
14 |
|
15 | var result = function async(bodyFunc) {
|
16 |
|
17 | var semaphore = config.maxConcurrency ? new Semaphore(config.maxConcurrency) : Semaphore.unlimited;
|
18 |
|
19 | var makeFunc = config.isIterable ? makeAsyncIterator : makeAsyncNonIterator;
|
20 | var result = makeFunc(bodyFunc, config, semaphore);
|
21 |
|
22 | var arity = bodyFunc.length;
|
23 | if (config.acceptsCallback)
|
24 | ++arity;
|
25 | result = makeFuncWithArity(result, arity);
|
26 | return result;
|
27 | };
|
28 |
|
29 | result.mod = makeModFunc(config);
|
30 | return result;
|
31 | }
|
32 |
|
33 | function makeAsyncIterator(bodyFunc, config, semaphore) {
|
34 |
|
35 | return function iterable() {
|
36 |
|
37 | var startupArgs = new Array(arguments.length + 1);
|
38 | for (var i = 0, len = arguments.length; i < len; ++i)
|
39 | startupArgs[i + 1] = arguments[i];
|
40 |
|
41 | var yield_ = function (expr) {
|
42 |
|
43 | if (!Fiber.current) {
|
44 | throw new Error('await functions, yield functions, and value-returning suspendable ' +
|
45 | 'functions may only be called from inside a suspendable function. ');
|
46 | }
|
47 |
|
48 | if (runContext.callback)
|
49 | runContext.callback(null, { value: expr, done: false });
|
50 | if (runContext.resolver)
|
51 | runContext.resolver.resolve({ value: expr, done: false });
|
52 | Fiber.yield();
|
53 | };
|
54 |
|
55 | startupArgs[0] = yield_;
|
56 |
|
57 | var runContext = new RunContext(bodyFunc, this, startupArgs);
|
58 | var iterator = new AsyncIterator(runContext, semaphore, config.returnValue, config.acceptsCallback);
|
59 |
|
60 | runContext.wrapped = function () {
|
61 | var len = arguments.length, args = new Array(len);
|
62 | for (var i = 0; i < len; ++i)
|
63 | args[i] = arguments[i];
|
64 | bodyFunc.apply(this, args);
|
65 | iterator.destroy();
|
66 | return { done: true };
|
67 | };
|
68 |
|
69 | return iterator;
|
70 | };
|
71 | }
|
72 |
|
73 | function makeAsyncNonIterator(bodyFunc, config, semaphore) {
|
74 |
|
75 | return function nonIterable() {
|
76 |
|
77 | var argsAsArray = new Array(arguments.length);
|
78 | for (var i = 0; i < argsAsArray.length; ++i)
|
79 | argsAsArray[i] = arguments[i];
|
80 |
|
81 | if (FiberMgr.isExecutingInFiber())
|
82 | this._semaphore = Semaphore.unlimited;
|
83 |
|
84 | var runContext = new RunContext(bodyFunc, this, argsAsArray, function () { return semaphore.leave(); });
|
85 | if (config.returnValue !== Config.NONE) {
|
86 | var resolver = defer();
|
87 | runContext.resolver = resolver;
|
88 | }
|
89 | if (config.acceptsCallback && argsAsArray.length && _.isFunction(argsAsArray[argsAsArray.length - 1])) {
|
90 | var callback = argsAsArray.pop();
|
91 | runContext.callback = callback;
|
92 | }
|
93 |
|
94 | if (config.returnValue === Config.THUNK) {
|
95 | var thunk = function (done) {
|
96 | if (done)
|
97 | resolver.promise.then(function (val) { return done(null, val); }, function (err) { return done(err); });
|
98 | semaphore.enter(function () { return FiberMgr.create().run(runContext); });
|
99 | };
|
100 | }
|
101 | else {
|
102 | semaphore.enter(function () { return FiberMgr.create().run(runContext); });
|
103 | }
|
104 |
|
105 | switch (config.returnValue) {
|
106 | case Config.PROMISE: return resolver.promise;
|
107 | case Config.THUNK: return thunk;
|
108 | case Config.RESULT: return await(resolver.promise);
|
109 | case Config.NONE: return;
|
110 | }
|
111 | };
|
112 | }
|
113 |
|
114 | function makeFuncWithArity(fn, arity) {
|
115 |
|
116 | switch (arity) {
|
117 | case 0: return function f0() { var i, l = arguments.length, r = new Array(l); for (i = 0; i < l; ++i)
|
118 | r[i] = arguments[i]; return fn.apply(this, r); };
|
119 | case 1: return function f1(a) { var i, l = arguments.length, r = new Array(l); for (i = 0; i < l; ++i)
|
120 | r[i] = arguments[i]; return fn.apply(this, r); };
|
121 | case 2: return function f2(a, b) { var i, l = arguments.length, r = new Array(l); for (i = 0; i < l; ++i)
|
122 | r[i] = arguments[i]; return fn.apply(this, r); };
|
123 | case 3: return function f3(a, b, c) { var i, l = arguments.length, r = new Array(l); for (i = 0; i < l; ++i)
|
124 | r[i] = arguments[i]; return fn.apply(this, r); };
|
125 | case 4: return function f4(a, b, c, d) { var i, l = arguments.length, r = new Array(l); for (i = 0; i < l; ++i)
|
126 | r[i] = arguments[i]; return fn.apply(this, r); };
|
127 | case 5: return function f5(a, b, c, d, e) { var i, l = arguments.length, r = new Array(l); for (i = 0; i < l; ++i)
|
128 | r[i] = arguments[i]; return fn.apply(this, r); };
|
129 | case 6: return function f6(a, b, c, d, e, f) { var i, l = arguments.length, r = new Array(l); for (i = 0; i < l; ++i)
|
130 | r[i] = arguments[i]; return fn.apply(this, r); };
|
131 | case 7: return function f7(a, b, c, d, e, f, g) { var i, l = arguments.length, r = new Array(l); for (i = 0; i < l; ++i)
|
132 | r[i] = arguments[i]; return fn.apply(this, r); };
|
133 | case 8: return function f8(a, b, c, d, e, f, g, h) { var i, l = arguments.length, r = new Array(l); for (i = 0; i < l; ++i)
|
134 | r[i] = arguments[i]; return fn.apply(this, r); };
|
135 | case 9: return function f9(a, b, c, d, e, f, g, h, _i) { var i, l = arguments.length, r = new Array(l); for (i = 0; i < l; ++i)
|
136 | r[i] = arguments[i]; return fn.apply(this, r); };
|
137 | default: return fn;
|
138 | }
|
139 | }
|
140 | function makeModFunc(config) {
|
141 | return function (options, maxConcurrency) {
|
142 | if (_.isString(options)) {
|
143 |
|
144 |
|
145 | var rt, cb, it;
|
146 | switch (options) {
|
147 | case 'returns: promise, callback: false, iterable: false':
|
148 | rt = 'promise';
|
149 | cb = false;
|
150 | it = false;
|
151 | break;
|
152 | case 'returns: thunk, callback: false, iterable: false':
|
153 | rt = 'thunk';
|
154 | cb = false;
|
155 | it = false;
|
156 | break;
|
157 | case 'returns: result, callback: false, iterable: false':
|
158 | rt = 'result';
|
159 | cb = false;
|
160 | it = false;
|
161 | break;
|
162 | case 'returns: promise, callback: true, iterable: false':
|
163 | rt = 'promise';
|
164 | cb = true;
|
165 | it = false;
|
166 | break;
|
167 | case 'returns: thunk, callback: true, iterable: false':
|
168 | rt = 'thunk';
|
169 | cb = true;
|
170 | it = false;
|
171 | break;
|
172 | case 'returns: result, callback: true, iterable: false':
|
173 | rt = 'result';
|
174 | cb = true;
|
175 | it = false;
|
176 | break;
|
177 | case 'returns: none, callback: true, iterable: false':
|
178 | rt = 'none';
|
179 | cb = true;
|
180 | it = false;
|
181 | break;
|
182 | case 'returns: promise, callback: false, iterable: true':
|
183 | rt = 'promise';
|
184 | cb = false;
|
185 | it = true;
|
186 | break;
|
187 | case 'returns: thunk, callback: false, iterable: true':
|
188 | rt = 'thunk';
|
189 | cb = false;
|
190 | it = true;
|
191 | break;
|
192 | case 'returns: result, callback: false, iterable: true':
|
193 | rt = 'result';
|
194 | cb = false;
|
195 | it = true;
|
196 | break;
|
197 | case 'returns: promise, callback: true, iterable: true':
|
198 | rt = 'promise';
|
199 | cb = true;
|
200 | it = true;
|
201 | break;
|
202 | case 'returns: thunk, callback: true, iterable: true':
|
203 | rt = 'thunk';
|
204 | cb = true;
|
205 | it = true;
|
206 | break;
|
207 | case 'returns: result, callback: true, iterable: true':
|
208 | rt = 'result';
|
209 | cb = true;
|
210 | it = true;
|
211 | break;
|
212 | case 'returns: none, callback: true, iterable: true':
|
213 | rt = 'none';
|
214 | cb = true;
|
215 | it = true;
|
216 | break;
|
217 | }
|
218 | options = { returnValue: rt, acceptsCallback: cb, isIterable: it, maxConcurrency: maxConcurrency };
|
219 | }
|
220 | var newConfig = new Config(_.defaults({}, options, config));
|
221 | return makeAsyncFunc(newConfig);
|
222 | };
|
223 | }
|
224 | module.exports = makeAsyncFunc;
|
225 |
|
\ | No newline at end of file |