-
Notifications
You must be signed in to change notification settings - Fork 396
/
debug.js
261 lines (223 loc) · 6.43 KB
/
debug.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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/** @license MIT License (c) copyright B Cavalier & J Hann */
/**
* This is a drop-in replacement for the when module that sets up automatic
* debug output for promises created or consumed by when.js. Use this
* instead of when to help with debugging.
*
* WARNING: This module **should never** be use this in a production environment.
* It exposes details of the promise
*
* In an AMD environment, you can simply change your path or package mappings:
*
* paths: {
* // 'when': 'path/to/when/when'
* 'when': 'path/to/when/debug'
* }
*
* or
*
* packages: [
* // { name: 'when', location: 'path/to/when', main: 'when' }
* { name: 'when', location: 'path/to/when', main: 'debug' }
* ]
*
* In a CommonJS environment, you can directly require this module where
* you would normally require 'when':
*
* // var when = require('when');
* var when = require('when/debug');
*
* Or you can temporarily modify the package.js to point main at debug.
* For example, when/package.json:
*
* ...
* "main": "./debug"
* ...
*
* @author brian@hovercraftstudios.com
*/
(function(define) {
define(['./when'], function(when) {
var promiseId, freeze, pending, exceptionsToRethrow, undef;
promiseId = 0;
freeze = Object.freeze || function(o) { return o; };
pending = {};
exceptionsToRethrow = {
RangeError: 1,
ReferenceError: 1,
SyntaxError: 1,
TypeError: 1
};
/**
* Setup debug output handlers for the supplied promise.
* @param p {Promise} A trusted (when.js) promise
* @return p
*/
function debugPromise(p) {
// TODO: Need to find a way for promises returned by .then()
// to also be debug promises.
p.then(
undef,
function(err) {
if(p.id) {
console.error(p.toString());
} else {
console.error('[object Promise] REJECTED:', err);
}
}
);
return p;
}
function wrapCallback(cb) {
if(typeof cb != 'function') return cb;
return function(v) {
try {
return cb(v);
} catch(err) {
if(err) {
if (err.name in exceptionsToRethrow) {
setTimeout(function() {
throw err;
}, 0);
} else if (err.stack) {
console.error(err.stack);
}
}
throw err;
}
}
}
/**
* Helper to form debug string for promises depending on their
* current state
* @param name
* @param id
* @param status
* @param value
*/
function toString(name, id, status, value) {
var s = '[object ' + name + ' ' + id + '] ' + status;
if(value !== pending) s += ': ' + value;
return s;
}
function F() {}
function beget(o) {
F.prototype = o;
o = new F();
F.prototype = undef;
return o;
}
/**
* Replacement for when() that sets up debug logging on the
* returned promise.
*/
function whenDebug() {
return debugPromise(when.apply(null, arguments));
}
/**
* Replacement for when.defer() that sets up debug logging
* on the created Deferred, its resolver, and its promise.
* @param [id] anything optional identifier for this Deferred that will show
* up in debug output
* @return {Deferred} a Deferred with debug logging
*/
function deferDebug() {
var d, status, value, origResolve, origReject, origThen, id;
// Delegate to create a Deferred;
d = when.defer();
status = 'pending';
value = pending;
// if no id provided, generate one. Not sure if this is
// useful or not.
id = arguments[arguments.length - 1];
if(id === undef) id = ++promiseId;
// Promise and resolver are frozen, so have to delegate
// in order to setup toString() on promise, resolver,
// and deferred
d.promise = beget(d.promise);
d.promise.toString = function() {
return toString('Promise', id, status, value);
};
d.resolver = beget(d.resolver);
d.resolver.toString = function() {
return toString('Resolver', id, status, value);
};
origResolve = d.resolver.resolve;
d.resolve = d.resolver.resolve = function(val) {
value = val;
status = 'resolving';
// console.log(d.resolver.toString());
return origResolve.apply(undef, arguments);
};
origReject = d.resolver.reject;
d.reject = d.resolver.reject = function(err) {
value = err;
status = 'REJECTING';
// console.error(d.resolver.toString());
return origReject.apply(undef, arguments);
};
d.toString = function() {
return toString('Deferred', id, status, value);
};
// Setup final state change handlers
d.then(
function(v) { status = 'resolved'; return v; },
function(e) { status = 'REJECTED'; throw e; }
);
// Experimenting with setting up ways to also debug promises returned
// by .then(). Also need to find a way to extend the id in a way that
// makes it obvious the returned promise is NOT the original, but is
// related to it--it's downstream in the promise chain.
origThen = d.promise.then;
d.then = d.promise.then = function(cb, eb, pb) {
var args, p, id;
id = d.id + '+';
args = [];
if(arguments.length > 0) args[0] = wrapCallback(cb, id);
if(arguments.length > 1) args[1] = wrapCallback(eb, id);
if(arguments.length > 2) args[2] = wrapCallback(pb, id);
var p = origThen.apply(null, args);
p.id = id;
p = beget(p);
p.toString = function() {
return toString('Promise', p.id, status, value);
};
// See below. Not sure if debug promises should be frozen
return freeze(p);
};
// Add an id to all directly created promises. It'd be great
// to find a way to propagate this id to promise created by .then()
d.id = d.promise.id = d.resolver.id = id;
// Attach debug handlers after the substitute promise
// has been setup, so the id can be logged.
debugPromise(d.promise);
// TODO: Should we still freeze these?
// Seems safer for now to err on the side of caution and freeze them,
// but it could be useful to all them to be modified during debugging.
freeze(d.promise);
freeze(d.resolver);
return d;
}
whenDebug.defer = deferDebug;
whenDebug.isPromise = when.isPromise;
// For each method we haven't already replaced, replace it with
// one that sets up debug logging on the returned promise
for(var p in when) {
if(when.hasOwnProperty(p) && !(p in whenDebug)) {
(function(p, orig) {
whenDebug[p] = function() {
return debugPromise(orig.apply(when, arguments));
};
})(p, when[p]);
}
}
return whenDebug;
});
})(typeof define == 'function'
? define
: function (deps, factory) { typeof module != 'undefined'
? (module.exports = factory(require('./when')))
: (this.when = factory(this.when));
}
// Boilerplate for AMD, Node, and browser global
);