Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 1cdfa3b

Browse files
committed
feat(deferreds/promises): Q-like deferred/promise implementation with a ton of specs
1 parent 16363d8 commit 1cdfa3b

File tree

7 files changed

+1072
-3
lines changed

7 files changed

+1072
-3
lines changed

angularFiles.js

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ angularFiles = {
99
'src/jqLite.js',
1010
'src/apis.js',
1111
'src/service/autoScroll.js',
12+
'src/Deferred.js',
1213
'src/service/browser.js',
1314
'src/service/cacheFactory.js',
1415
'src/service/compiler.js',

src/AngularPublic.js

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ function ngModule($provide, $injector) {
8686
$provide.service('$route', $RouteProvider);
8787
$provide.service('$routeParams', $RouteParamsProvider);
8888
$provide.service('$rootScope', $RootScopeProvider);
89+
$provide.service('$q', $QProvider);
8990
$provide.service('$sniffer', $SnifferProvider);
9091
$provide.service('$templateCache', $TemplateCacheProvider);
9192
$provide.service('$window', $WindowProvider);

src/Deferred.js

+206
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
'use strict';
2+
3+
/**
4+
* inspired by Kris Kowal's Q (https://github.com/kriskowal/q)
5+
*/
6+
7+
/**
8+
* Constructs a promise manager.
9+
*
10+
* @param {function(function)=} nextTick Function for executing functions in the next turn. Falls
11+
* back to `setTimeout` if undefined.
12+
* @param {function(...*)=} exceptionHandler Function into which unexpected exceptions are passed for
13+
* debugging purposes. Falls back to `console.error` if undefined,
14+
* @returns {object} Promise manager.
15+
*/
16+
function qFactory(nextTick, exceptionHandler) {
17+
18+
nextTick = nextTick || function(callback) {
19+
setTimeout(callback, 0); // very rare since most of queueing will be handled within $apply
20+
};
21+
22+
exceptionHandler = exceptionHandler || function(e) {
23+
// TODO(i): console.error is somehow reset to function(a) {}, it might be a JSTD bug
24+
if (console && console.log) {
25+
console.log(e);
26+
}
27+
}
28+
29+
var defer = function() {
30+
var pending = [],
31+
value, deferred;
32+
33+
deferred = {
34+
35+
resolve: function(val) {
36+
if (pending) {
37+
var callbacks = pending;
38+
pending = undefined;
39+
value = ref(val);
40+
41+
if (callbacks.length) {
42+
nextTick(function() {
43+
var callback;
44+
for (var i = 0, ii = callbacks.length; i < ii; i++) {
45+
callback = callbacks[i];
46+
value.then(callback[0], callback[1]);
47+
}
48+
});
49+
}
50+
}
51+
},
52+
53+
54+
reject: function(reason) {
55+
deferred.resolve(reject(reason));
56+
},
57+
58+
59+
promise: {
60+
then: function(callback, errback) {
61+
var result = defer();
62+
63+
var wrappedCallback = function(value) {
64+
try {
65+
result.resolve((callback || defaultCallback)(value));
66+
} catch(e) {
67+
exceptionHandler(e);
68+
result.reject(e);
69+
}
70+
};
71+
72+
var wrappedErrback = function(reason) {
73+
try {
74+
result.resolve((errback || defaultErrback)(reason));
75+
} catch(e) {
76+
exceptionHandler(e);
77+
result.reject(e);
78+
}
79+
};
80+
81+
if (pending) {
82+
pending.push([wrappedCallback, wrappedErrback]);
83+
} else {
84+
value.then(wrappedCallback, wrappedErrback);
85+
}
86+
87+
return result.promise;
88+
}
89+
}
90+
};
91+
92+
return deferred;
93+
};
94+
95+
96+
var ref = function(value) {
97+
if (value && value.then) return value;
98+
return {
99+
then: function(callback) {
100+
var result = defer();
101+
nextTick(function() {
102+
result.resolve(callback(value));
103+
});
104+
return result.promise;
105+
}
106+
};
107+
};
108+
109+
110+
var reject = function(reason) {
111+
return {
112+
then: function(callback, errback) {
113+
var result = defer();
114+
nextTick(function() {
115+
result.resolve(errback(reason));
116+
});
117+
return result.promise;
118+
}
119+
};
120+
};
121+
122+
123+
var when = function(value, callback, errback) {
124+
var result = defer(),
125+
done;
126+
127+
var wrappedCallback = function(value) {
128+
try {
129+
return (callback || defaultCallback)(value);
130+
} catch (e) {
131+
exceptionHandler(e);
132+
return reject(e);
133+
}
134+
};
135+
136+
var wrappedErrback = function(reason) {
137+
try {
138+
return (errback || defaultErrback)(reason);
139+
} catch (e) {
140+
exceptionHandler(e);
141+
return reject(e);
142+
}
143+
};
144+
145+
nextTick(function() {
146+
ref(value).then(function(value) {
147+
if (done) return;
148+
done = true;
149+
result.resolve(ref(value).then(wrappedCallback, wrappedErrback));
150+
}, function(reason) {
151+
if (done) return;
152+
done = true;
153+
result.resolve(wrappedErrback(reason));
154+
});
155+
});
156+
157+
return result.promise;
158+
};
159+
160+
161+
function defaultCallback(value) {
162+
return value;
163+
}
164+
165+
166+
function defaultErrback(reason) {
167+
return reject(reason);
168+
}
169+
170+
171+
function all(promises) {
172+
var deferred = defer(),
173+
counter = promises.length,
174+
results = [];
175+
176+
forEach(promises, function(promise, index) {
177+
promise.then(function(value) {
178+
if (index in results) return;
179+
results[index] = value;
180+
if (!(--counter)) deferred.resolve(results);
181+
}, function(reason) {
182+
if (index in results) return;
183+
deferred.reject(reason);
184+
});
185+
});
186+
187+
return deferred.promise;
188+
}
189+
190+
return {
191+
defer: defer,
192+
reject: reject,
193+
when: when,
194+
all: all
195+
};
196+
}
197+
198+
// TODO(i): move elsewhere
199+
function $QProvider() {
200+
201+
this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
202+
return qFactory(function(callback) {
203+
$rootScope.$evalAsync(callback);
204+
}, $exceptionHandler);
205+
}];
206+
}

src/service/http.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ function $HttpProvider() {
229229
/**
230230
* Represents Request object, returned by $http()
231231
*
232-
* !!! ACCESS CLOSURE VARS:
232+
* !!! ACCESSES CLOSURE VARS:
233233
* $httpBackend, $browser, $config, $log, $rootScope, defaultCache, $http.pendingRequests
234234
*/
235235
function XhrFuture() {

src/service/scope.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -381,11 +381,11 @@ function $RootScopeProvider(){
381381
}
382382
} while ((current = next));
383383

384-
if(!(ttl--)) {
384+
if(dirty && !(ttl--)) {
385385
throw Error('100 $digest() iterations reached. Aborting!\n' +
386386
'Watchers fired in the last 5 iterations: ' + toJson(watchLog));
387387
}
388-
} while (dirty);
388+
} while (dirty || asyncQueue.length);
389389
},
390390

391391
/**

0 commit comments

Comments
 (0)