-
Notifications
You must be signed in to change notification settings - Fork 5
/
avow.js
215 lines (177 loc) · 5.23 KB
/
avow.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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/* Copyright (c) 2012-2014 Brian Cavalier */
(function(define, global) {
define(function() {
var avow, enqueue, defaultConfig, setTimeout, bind, uncurryThis, call, undef;
bind = Function.prototype.bind;
uncurryThis = bind.bind(bind.call);
call = uncurryThis(bind.call);
// Prefer setImmediate, cascade to node, vertx and finally setTimeout
/*global setImmediate,process,vertx*/
setTimeout = global.setTimeout;
enqueue = typeof setImmediate === 'function' ? setImmediate.bind(global)
: typeof process === 'object' && process.nextTick ? process.nextTick
: typeof vertx === 'object' ? vertx.runOnLoop // vert.x
: function(task) { setTimeout(task, 0); }; // fallback
// Default configuration
defaultConfig = {
enqueue: enqueue,
unhandled: noop,
handled: noop,
protect: noop
};
// Create the default module instance
// This is what you get when you require('avow')
avow = constructAvow(defaultConfig);
// You can use require('avow').construct(options) to
// construct a custom configured version of avow
avow.construct = constructAvow;
return avow;
// This constructs configured instances of the avow module
function constructAvow(config) {
var enqueue, onHandled, onUnhandled, protect;
// Grab the config params, use defaults where necessary
enqueue = config.enqueue || defaultConfig.enqueue;
onHandled = config.handled || defaultConfig.handled;
onUnhandled = config.unhandled || defaultConfig.unhandled;
protect = config.protect || defaultConfig.protect;
// Add lift and reject methods.
promise.lift = lift;
promise.reject = reject;
return promise;
// Return a trusted promise for x. Where if x is a
// - Promise, return it
// - value, return a promise that will eventually fulfill with x
// - thenable, assimilate it and return a promise whose fate follows that of x.
function lift(x) {
return promise(function(resolve) {
resolve(x);
});
}
// Return a rejected promise
function reject(reason) {
return promise(function(_, reject) {
reject(reason);
});
}
// Return a pending promise whose fate is determined by resolver
function promise(resolver) {
var self, value, handled, handlers = [];
self = new Promise(then);
// Call the resolver to seal the promise's fate
try {
resolver(promiseResolve, promiseReject);
} catch(e) {
promiseReject(e);
}
// Return the promise
return self;
// Register handlers with this promise
function then(onFulfilled, onRejected) {
if (!handled) {
handled = true;
onHandled(self);
}
return promise(function(resolve, reject) {
handlers
// Call handlers later, after resolution
? handlers.push(function(value) {
value.then(onFulfilled, onRejected).then(resolve, reject);
})
// Call handlers soon, but not in the current stack
: enqueue(function() {
value.then(onFulfilled, onRejected).then(resolve, reject);
});
});
}
// Resolve with a value, promise, or thenable
function promiseResolve(value) {
if(!handlers) {
return;
}
resolve(coerce(value));
}
// Reject with reason verbatim
function promiseReject(reason) {
if(!handlers) {
return;
}
if(!handled) {
onUnhandled(self, reason);
}
resolve(rejected(reason));
}
// For all handlers, run the Promise Resolution Procedure on this promise
function resolve(x) {
var queue = handlers;
handlers = undef;
value = x;
enqueue(function () {
queue.forEach(function (handler) {
handler(value);
});
});
}
}
// Private
// Trusted promise constructor
function Promise(then) {
this.then = then;
protect(this);
}
// Coerce x to a promise
function coerce(x) {
if(x instanceof Promise) {
return x;
} else if (x !== Object(x)) {
return fulfilled(x);
}
return promise(function(resolve, reject) {
enqueue(function() {
try {
// We must check and assimilate in the same tick, but not the
// current tick, careful only to access promiseOrValue.then once.
var untrustedThen = x.then;
if(typeof untrustedThen === 'function') {
// Prevent thenable self-cycles
call(untrustedThen, x, function(result) {
resolve(result === x ? fulfilled(x) : result);
}, reject);
} else {
// It's a value, create a fulfilled wrapper
resolve(fulfilled(x));
}
} catch(e) {
// Something went wrong, reject
reject(e);
}
});
});
}
// create an already-fulfilled promise used to break assimilation recursion
function fulfilled(x) {
var self = new Promise(function (onFulfilled) {
try {
return typeof onFulfilled == 'function'
? coerce(onFulfilled(x)) : self;
} catch (e) {
return rejected(e);
}
});
return self;
}
// create an already-rejected promise
function rejected(x) {
var self = new Promise(function (_, onRejected) {
try {
return typeof onRejected == 'function'
? coerce(onRejected(x)) : self;
} catch (e) {
return rejected(e);
}
});
return self;
}
}
function noop() {}
});
})(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }, this);