UNPKG

6.07 kBJavaScriptView Raw
1var FiberMgr = require('./fiberManager');
2var Semaphore = require('./semaphore');
3var Config = require('./config');
4var defer = require('./defer');
5var await = require('../await/index');
6/**
7 * Asynchronous analogue to an ES6 Iterator. Rather than return each value/done
8 * result synchronously, the next() function notifies a promise and/or callback
9 * when the next result is ready.
10 */
11var AsyncIterator = (function () {
12 /** Construct a new AsyncIterator instance. This will create a fiber. */
13 function AsyncIterator(runContext, semaphore, returnValue, acceptsCallback) {
14 this._runContext = runContext;
15 this._semaphore = semaphore;
16 this._fiber = FiberMgr.create();
17 this._returnValue = returnValue;
18 this._acceptsCallback = acceptsCallback;
19 }
20 /** Fetch the next result from the iterator. */
21 AsyncIterator.prototype.next = function (callback) {
22 var _this = this;
23 // Configure the run context.
24 if (this._acceptsCallback) {
25 this._runContext.callback = callback; // May be null, in which case it won't be used.
26 }
27 if (this._returnValue !== Config.NONE) {
28 var resolver = defer();
29 this._runContext.resolver = resolver;
30 }
31 // Remove concurrency restrictions for nested calls, to avoid race conditions.
32 if (FiberMgr.isExecutingInFiber())
33 this._semaphore = Semaphore.unlimited;
34 // Run the fiber until it either yields a value or completes. For thunks, this is a lazy operation.
35 if (this._returnValue === Config.THUNK) {
36 var thunk = function (done) {
37 if (done)
38 resolver.promise.then(function (val) { return done(null, val); }, function (err) { return done(err); });
39 _this._semaphore.enter(function () { return _this._fiber.run(_this._runContext); });
40 _this._runContext.done = function () { return _this._semaphore.leave(); };
41 };
42 }
43 else {
44 this._semaphore.enter(function () { return _this._fiber.run(_this._runContext); });
45 this._runContext.done = function () { return _this._semaphore.leave(); };
46 }
47 // Return the appropriate value.
48 switch (this._returnValue) {
49 case Config.PROMISE: return resolver.promise;
50 case Config.THUNK: return thunk;
51 case Config.RESULT: return await(resolver.promise);
52 case Config.NONE: return;
53 }
54 };
55 /** Enumerate the entire iterator, calling callback with each result. */
56 AsyncIterator.prototype.forEach = function (callback, doneCallback) {
57 var _this = this;
58 // Create a function that calls next() in an asynchronous loop until the iteration is complete.
59 var run, runCtx = this._runContext;
60 if (this._returnValue === Config.RESULT)
61 run = function () { return stepAwaited(function () { return _this.next(); }); };
62 else if (this._returnValue === Config.THUNK)
63 run = function () { return _this.next()(stepCallback); };
64 else if (this._acceptsCallback)
65 run = function () { return _this.next(stepCallback); };
66 else
67 run = function () { return _this.next().then(stepResolved, endOfIteration); };
68 // Configure the resolver and callback to be invoked at the end of the iteration.
69 if (this._returnValue === Config.PROMISE || this._returnValue === Config.THUNK) {
70 var doneResolver = defer();
71 }
72 if (!this._acceptsCallback)
73 doneCallback = null;
74 // Execute the entire iteration. For thunks, this is a lazy operation.
75 if (this._returnValue === Config.THUNK) {
76 var thunk = function (done) {
77 if (done)
78 doneResolver.promise.then(function (val) { return done(null, val); }, function (err) { return done(err); });
79 run();
80 };
81 }
82 else {
83 run();
84 }
85 // Return the appropriate value.
86 switch (this._returnValue) {
87 case Config.PROMISE: return doneResolver.promise;
88 case Config.THUNK: return thunk;
89 case Config.RESULT: return undefined;
90 case Config.NONE: return undefined;
91 }
92 // These functions handle stepping through and finalising the iteration.
93 function stepAwaited(next) {
94 try {
95 while (true) {
96 var item = next();
97 if (item.done)
98 return endOfIteration();
99 callback(item.value);
100 }
101 }
102 catch (err) {
103 endOfIteration(err);
104 throw err;
105 }
106 }
107 function stepCallback(err, result) {
108 if (err || result.done)
109 return endOfIteration(err);
110 callback(result.value);
111 setImmediate(run);
112 }
113 function stepResolved(result) {
114 if (result.done)
115 return endOfIteration();
116 callback(result.value);
117 setImmediate(run);
118 }
119 function endOfIteration(err) {
120 if (doneCallback)
121 err ? doneCallback(err) : doneCallback();
122 if (doneResolver) {
123 if (FiberMgr.isExecutingInFiber()) {
124 runCtx.resolver = doneResolver; // FiberManager will handle it
125 }
126 else {
127 err ? doneResolver.reject(err) : doneResolver.resolve(null);
128 }
129 }
130 }
131 };
132 /** Release resources associated with this object (i.e., the fiber). */
133 AsyncIterator.prototype.destroy = function () {
134 this._fiber = null;
135 };
136 return AsyncIterator;
137})();
138module.exports = AsyncIterator;
139//# sourceMappingURL=asyncIterator.js.map
\No newline at end of file