source: imaps-frontend/node_modules/gensync/index.js@ d565449

main
Last change on this file since d565449 was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 9.4 KB
Line 
1"use strict";
2
3// These use the global symbol registry so that multiple copies of this
4// library can work together in case they are not deduped.
5const GENSYNC_START = Symbol.for("gensync:v1:start");
6const GENSYNC_SUSPEND = Symbol.for("gensync:v1:suspend");
7
8const GENSYNC_EXPECTED_START = "GENSYNC_EXPECTED_START";
9const GENSYNC_EXPECTED_SUSPEND = "GENSYNC_EXPECTED_SUSPEND";
10const GENSYNC_OPTIONS_ERROR = "GENSYNC_OPTIONS_ERROR";
11const GENSYNC_RACE_NONEMPTY = "GENSYNC_RACE_NONEMPTY";
12const GENSYNC_ERRBACK_NO_CALLBACK = "GENSYNC_ERRBACK_NO_CALLBACK";
13
14module.exports = Object.assign(
15 function gensync(optsOrFn) {
16 let genFn = optsOrFn;
17 if (typeof optsOrFn !== "function") {
18 genFn = newGenerator(optsOrFn);
19 } else {
20 genFn = wrapGenerator(optsOrFn);
21 }
22
23 return Object.assign(genFn, makeFunctionAPI(genFn));
24 },
25 {
26 all: buildOperation({
27 name: "all",
28 arity: 1,
29 sync: function(args) {
30 const items = Array.from(args[0]);
31 return items.map(item => evaluateSync(item));
32 },
33 async: function(args, resolve, reject) {
34 const items = Array.from(args[0]);
35
36 if (items.length === 0) {
37 Promise.resolve().then(() => resolve([]));
38 return;
39 }
40
41 let count = 0;
42 const results = items.map(() => undefined);
43 items.forEach((item, i) => {
44 evaluateAsync(
45 item,
46 val => {
47 results[i] = val;
48 count += 1;
49
50 if (count === results.length) resolve(results);
51 },
52 reject
53 );
54 });
55 },
56 }),
57 race: buildOperation({
58 name: "race",
59 arity: 1,
60 sync: function(args) {
61 const items = Array.from(args[0]);
62 if (items.length === 0) {
63 throw makeError("Must race at least 1 item", GENSYNC_RACE_NONEMPTY);
64 }
65
66 return evaluateSync(items[0]);
67 },
68 async: function(args, resolve, reject) {
69 const items = Array.from(args[0]);
70 if (items.length === 0) {
71 throw makeError("Must race at least 1 item", GENSYNC_RACE_NONEMPTY);
72 }
73
74 for (const item of items) {
75 evaluateAsync(item, resolve, reject);
76 }
77 },
78 }),
79 }
80);
81
82/**
83 * Given a generator function, return the standard API object that executes
84 * the generator and calls the callbacks.
85 */
86function makeFunctionAPI(genFn) {
87 const fns = {
88 sync: function(...args) {
89 return evaluateSync(genFn.apply(this, args));
90 },
91 async: function(...args) {
92 return new Promise((resolve, reject) => {
93 evaluateAsync(genFn.apply(this, args), resolve, reject);
94 });
95 },
96 errback: function(...args) {
97 const cb = args.pop();
98 if (typeof cb !== "function") {
99 throw makeError(
100 "Asynchronous function called without callback",
101 GENSYNC_ERRBACK_NO_CALLBACK
102 );
103 }
104
105 let gen;
106 try {
107 gen = genFn.apply(this, args);
108 } catch (err) {
109 cb(err);
110 return;
111 }
112
113 evaluateAsync(gen, val => cb(undefined, val), err => cb(err));
114 },
115 };
116 return fns;
117}
118
119function assertTypeof(type, name, value, allowUndefined) {
120 if (
121 typeof value === type ||
122 (allowUndefined && typeof value === "undefined")
123 ) {
124 return;
125 }
126
127 let msg;
128 if (allowUndefined) {
129 msg = `Expected opts.${name} to be either a ${type}, or undefined.`;
130 } else {
131 msg = `Expected opts.${name} to be a ${type}.`;
132 }
133
134 throw makeError(msg, GENSYNC_OPTIONS_ERROR);
135}
136function makeError(msg, code) {
137 return Object.assign(new Error(msg), { code });
138}
139
140/**
141 * Given an options object, return a new generator that dispatches the
142 * correct handler based on sync or async execution.
143 */
144function newGenerator({ name, arity, sync, async, errback }) {
145 assertTypeof("string", "name", name, true /* allowUndefined */);
146 assertTypeof("number", "arity", arity, true /* allowUndefined */);
147 assertTypeof("function", "sync", sync);
148 assertTypeof("function", "async", async, true /* allowUndefined */);
149 assertTypeof("function", "errback", errback, true /* allowUndefined */);
150 if (async && errback) {
151 throw makeError(
152 "Expected one of either opts.async or opts.errback, but got _both_.",
153 GENSYNC_OPTIONS_ERROR
154 );
155 }
156
157 if (typeof name !== "string") {
158 let fnName;
159 if (errback && errback.name && errback.name !== "errback") {
160 fnName = errback.name;
161 }
162 if (async && async.name && async.name !== "async") {
163 fnName = async.name.replace(/Async$/, "");
164 }
165 if (sync && sync.name && sync.name !== "sync") {
166 fnName = sync.name.replace(/Sync$/, "");
167 }
168
169 if (typeof fnName === "string") {
170 name = fnName;
171 }
172 }
173
174 if (typeof arity !== "number") {
175 arity = sync.length;
176 }
177
178 return buildOperation({
179 name,
180 arity,
181 sync: function(args) {
182 return sync.apply(this, args);
183 },
184 async: function(args, resolve, reject) {
185 if (async) {
186 async.apply(this, args).then(resolve, reject);
187 } else if (errback) {
188 errback.call(this, ...args, (err, value) => {
189 if (err == null) resolve(value);
190 else reject(err);
191 });
192 } else {
193 resolve(sync.apply(this, args));
194 }
195 },
196 });
197}
198
199function wrapGenerator(genFn) {
200 return setFunctionMetadata(genFn.name, genFn.length, function(...args) {
201 return genFn.apply(this, args);
202 });
203}
204
205function buildOperation({ name, arity, sync, async }) {
206 return setFunctionMetadata(name, arity, function*(...args) {
207 const resume = yield GENSYNC_START;
208 if (!resume) {
209 // Break the tail call to avoid a bug in V8 v6.X with --harmony enabled.
210 const res = sync.call(this, args);
211 return res;
212 }
213
214 let result;
215 try {
216 async.call(
217 this,
218 args,
219 value => {
220 if (result) return;
221
222 result = { value };
223 resume();
224 },
225 err => {
226 if (result) return;
227
228 result = { err };
229 resume();
230 }
231 );
232 } catch (err) {
233 result = { err };
234 resume();
235 }
236
237 // Suspend until the callbacks run. Will resume synchronously if the
238 // callback was already called.
239 yield GENSYNC_SUSPEND;
240
241 if (result.hasOwnProperty("err")) {
242 throw result.err;
243 }
244
245 return result.value;
246 });
247}
248
249function evaluateSync(gen) {
250 let value;
251 while (!({ value } = gen.next()).done) {
252 assertStart(value, gen);
253 }
254 return value;
255}
256
257function evaluateAsync(gen, resolve, reject) {
258 (function step() {
259 try {
260 let value;
261 while (!({ value } = gen.next()).done) {
262 assertStart(value, gen);
263
264 // If this throws, it is considered to have broken the contract
265 // established for async handlers. If these handlers are called
266 // synchronously, it is also considered bad behavior.
267 let sync = true;
268 let didSyncResume = false;
269 const out = gen.next(() => {
270 if (sync) {
271 didSyncResume = true;
272 } else {
273 step();
274 }
275 });
276 sync = false;
277
278 assertSuspend(out, gen);
279
280 if (!didSyncResume) {
281 // Callback wasn't called synchronously, so break out of the loop
282 // and let it call 'step' later.
283 return;
284 }
285 }
286
287 return resolve(value);
288 } catch (err) {
289 return reject(err);
290 }
291 })();
292}
293
294function assertStart(value, gen) {
295 if (value === GENSYNC_START) return;
296
297 throwError(
298 gen,
299 makeError(
300 `Got unexpected yielded value in gensync generator: ${JSON.stringify(
301 value
302 )}. Did you perhaps mean to use 'yield*' instead of 'yield'?`,
303 GENSYNC_EXPECTED_START
304 )
305 );
306}
307function assertSuspend({ value, done }, gen) {
308 if (!done && value === GENSYNC_SUSPEND) return;
309
310 throwError(
311 gen,
312 makeError(
313 done
314 ? "Unexpected generator completion. If you get this, it is probably a gensync bug."
315 : `Expected GENSYNC_SUSPEND, got ${JSON.stringify(
316 value
317 )}. If you get this, it is probably a gensync bug.`,
318 GENSYNC_EXPECTED_SUSPEND
319 )
320 );
321}
322
323function throwError(gen, err) {
324 // Call `.throw` so that users can step in a debugger to easily see which
325 // 'yield' passed an unexpected value. If the `.throw` call didn't throw
326 // back to the generator, we explicitly do it to stop the error
327 // from being swallowed by user code try/catches.
328 if (gen.throw) gen.throw(err);
329 throw err;
330}
331
332function isIterable(value) {
333 return (
334 !!value &&
335 (typeof value === "object" || typeof value === "function") &&
336 !value[Symbol.iterator]
337 );
338}
339
340function setFunctionMetadata(name, arity, fn) {
341 if (typeof name === "string") {
342 // This should always work on the supported Node versions, but for the
343 // sake of users that are compiling to older versions, we check for
344 // configurability so we don't throw.
345 const nameDesc = Object.getOwnPropertyDescriptor(fn, "name");
346 if (!nameDesc || nameDesc.configurable) {
347 Object.defineProperty(
348 fn,
349 "name",
350 Object.assign(nameDesc || {}, {
351 configurable: true,
352 value: name,
353 })
354 );
355 }
356 }
357
358 if (typeof arity === "number") {
359 const lengthDesc = Object.getOwnPropertyDescriptor(fn, "length");
360 if (!lengthDesc || lengthDesc.configurable) {
361 Object.defineProperty(
362 fn,
363 "length",
364 Object.assign(lengthDesc || {}, {
365 configurable: true,
366 value: arity,
367 })
368 );
369 }
370 }
371
372 return fn;
373}
Note: See TracBrowser for help on using the repository browser.