-
Notifications
You must be signed in to change notification settings - Fork 6
/
index.js
152 lines (128 loc) · 3.08 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
var Promise = require('any-promise');
var co = require('co');
module.exports = compose;
compose.Wrap = Wrap;
function compose(middleware) {
return function (next) {
next = next || new Wrap(noop);
var i = middleware.length;
while (i--) next = new Wrap(middleware[i], this, next);
return next
}
}
/**
* Wrap a function, then lazily call it,
* always returning both a promise and a generator.
*
* @param {Function} fn
* @param {Object} ctx
* @param {Wrap} next
*/
function Wrap(fn, ctx, next) {
if (typeof fn !== 'function') throw TypeError('Not a function!');
this._fn = fn;
this._ctx = ctx;
this._next = next;
this._called = false;
this._value = undefined;
this._promise = undefined;
this._generator = undefined;
}
/**
* Lazily call the function.
* Note that if it's not an async or generator function,
*
* @returns {Mixed}
*/
Wrap.prototype._getValue = function () {
if (!this._called) {
this._called = true;
try {
this._value = this._fn.call(this._ctx, this._next);
} catch (e) {
this._value = Promise.reject(e);
}
}
return this._value
};
/**
* Lazily create a promise from the return value.
*
* @returns {Promise}
*/
Wrap.prototype._getPromise = function () {
if (this._promise === undefined) {
var value = this._getValue();
this._promise = isGenerator(value)
? co.call(this._ctx, value)
: Promise.resolve(value);
}
return this._promise
}
/**
* Lazily create a generator from the return value.
*
* @returns {Iterator}
*/
Wrap.prototype._getGenerator = function () {
if (this._generator === undefined) {
var value = this._getValue();
this._generator = isGenerator(value)
? value
: promiseToGenerator.call(this._ctx, value);
}
return this._generator
}
/**
* In later version of v8,
* `yield*` works on the `[@@iterator]` method.
*
* @returns {Iterator}
*/
if (typeof Symbol !== 'undefined') {
Wrap.prototype[Symbol.iterator] = function () {
return this._getGenerator();
}
}
/**
* This creates a generator from a promise or a value.
*
* TODO: try to avoid using a generator function for this.
*
* @returns {Iterator}
*/
var loggedDeprecationMessage = false;
function* promiseToGenerator(promise) {
if (!loggedDeprecationMessage) {
console.error('A promise was converted into a generator, which is an anti-pattern. Please avoid using `yield* next`!')
loggedDeprecationMessage = true;
}
return yield Promise.resolve(promise);
}
/**
* Proxy generator and promise methods.
*/
Wrap.prototype.then = function (resolve, reject) {
return this._getPromise().then(resolve, reject);
}
Wrap.prototype.catch = function (reject) {
return this._getPromise().catch(reject);
}
Wrap.prototype.next = function (val) {
return this._getGenerator().next(val);
}
Wrap.prototype.throw = function (err) {
return this._getGenerator().throw(err);
}
/**
* Check if `obj` is a generator.
*
* @param {Mixed} obj
* @return {Boolean}
*/
function isGenerator(obj) {
return obj
&& typeof obj.next === 'function'
&& typeof obj.throw === 'function';
}
function noop() {}