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

Commit 01a909f

Browse files
committed
perf($q): reduce memory footprint
Reduce the memory footprint of $q by using common functions that will be used by all promises.
1 parent c9c4636 commit 01a909f

File tree

2 files changed

+161
-129
lines changed

2 files changed

+161
-129
lines changed

lib/promises-aplus/promises-aplus-test-adapter.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
var isFunction = function isFunction(value){return typeof value == 'function';}
22
var isObject = function isObject(value){return value != null && typeof value === 'object';}
3+
function bind(self, fn) {
4+
var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
5+
if (isFunction(fn) && !(fn instanceof RegExp)) {
6+
return curryArgs.length
7+
? function() {
8+
return arguments.length
9+
? fn.apply(self, curryArgs.concat(slice.call(arguments, 0)))
10+
: fn.apply(self, curryArgs);
11+
}
12+
: function() {
13+
return arguments.length
14+
? fn.apply(self, arguments)
15+
: fn.call(self);
16+
};
17+
} else {
18+
// in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
19+
return fn;
20+
}
21+
}
22+
323

424
var $q = qFactory(process.nextTick, function noopExceptionHandler() {});
525

src/ng/q.js

Lines changed: 141 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -202,154 +202,166 @@ function qFactory(nextTick, exceptionHandler) {
202202
return [wrap(resolveFn), wrap(rejectFn)];
203203
}
204204

205-
/**
206-
* @ngdoc
207-
* @name ng.$q#defer
208-
* @methodOf ng.$q
209-
* @description
210-
* Creates a `Deferred` object which represents a task which will finish in the future.
211-
*
212-
* @returns {Deferred} Returns a new instance of deferred.
213-
*/
214-
var defer = function() {
215-
var pending = [],
216-
value,
217-
status = 0,
218-
deferred,
219-
processScheduled = false;
220-
221-
function processQueue() {
222-
var fn, promise;
223-
224-
processScheduled = false;
225-
if (!status) return;
226-
for (var i = 0; i < pending.length; ++i) {
227-
promise = pending[i][0];
228-
fn = pending[i][status];
229-
try {
230-
if (isFunction(fn)) {
231-
promise.resolve(fn(value));
232-
} else if (status === 1) {
233-
promise.resolve(value);
234-
} else {
235-
promise.reject(value);
236-
}
237-
} catch(e) {
238-
promise.reject(e);
239-
exceptionHandler(e);
240-
}
241-
}
242-
pending = [];
205+
function makePromise(value, resolved) {
206+
var result = defer();
207+
if (resolved) {
208+
result.resolve(value);
209+
} else {
210+
result.reject(value);
243211
}
212+
return result.promise;
213+
}
244214

245-
function scheduleProcessQueue() {
246-
if (processScheduled) return;
247-
processScheduled = true;
248-
nextTick(processQueue);
215+
function handleCallback(value, isResolved, callback) {
216+
var callbackOutput = null;
217+
try {
218+
if (isFunction(callback)) callbackOutput = callback();
219+
} catch(e) {
220+
return makePromise(e, false);
249221
}
222+
if (callbackOutput && isFunction(callbackOutput.then)) {
223+
return callbackOutput.then(function() {
224+
return makePromise(value, isResolved);
225+
}, function(error) {
226+
return makePromise(error, false);
227+
});
228+
} else {
229+
return makePromise(value, isResolved);
230+
}
231+
}
232+
250233

251-
function resolve(val) {
252-
var then, fns;
234+
function processQueue(state) {
235+
var fn, promise;
253236

254-
if (status) return;
255-
if (val === deferred.promise) throw new TypeError('Cycle detected');
256-
fns = callOnce(resolve, reject);
237+
state.processScheduled = false;
238+
if (!state.status) return;
239+
for (var i = 0; i < state.pending.length; ++i) {
240+
promise = state.pending[i][0];
241+
fn = state.pending[i][state.status];
257242
try {
258-
if ((isObject(val) || isFunction(val))) then = val && val.then;
259-
if (isFunction(then)) {
260-
then.call(val, fns[0], fns[1], deferred.notify);
243+
if (isFunction(fn)) {
244+
promise.resolve(fn(state.value));
245+
} else if (state.status === 1) {
246+
promise.resolve(state.value);
261247
} else {
262-
value = val;
263-
status = 1;
264-
scheduleProcessQueue();
248+
promise.reject(state.value);
265249
}
266250
} catch(e) {
267-
fns[1](e);
251+
promise.reject(e);
268252
exceptionHandler(e);
269253
}
270254
}
255+
state.pending = [];
256+
}
257+
258+
function scheduleProcessQueue(state) {
259+
if (state.processScheduled) return;
260+
state.processScheduled = true;
261+
nextTick(function() { processQueue(state); });
262+
}
263+
264+
function Promise(state) {
265+
this.$$state = state;
266+
}
267+
268+
Promise.prototype.then = function(onFulfilled, onRejected, progressBack) {
269+
var result = defer();
270+
271+
this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
272+
if (this.$$state.status) scheduleProcessQueue(this.$$state);
273+
274+
return result.promise;
275+
};
276+
277+
Promise.prototype["catch"] = function(callback) {
278+
return this.then(null, callback);
279+
};
280+
281+
Promise.prototype["finally"] = function(callback, progressBack) {
282+
return this.then(function(value) {
283+
return handleCallback(value, true, callback);
284+
}, function(error) {
285+
return handleCallback(error, false, callback);
286+
}, progressBack);
287+
};
288+
289+
290+
function Defer() {
291+
this.$$state = {
292+
pending: [],
293+
status: 0,
294+
value: undefined,
295+
processScheduled: false
296+
};
297+
this.promise = new Promise(this.$$state);
298+
}
299+
300+
Defer.prototype.resolve = function(val) {
301+
var then, fns;
271302

272-
function reject(reason) {
273-
if (status) return;
274-
value = reason;
275-
status = 2;
276-
scheduleProcessQueue();
303+
if (this.$$state.status) return;
304+
if (val === this.promise) throw new TypeError('Cycle detected');
305+
fns = callOnce(this.resolve, this.reject);
306+
try {
307+
if ((isObject(val) || isFunction(val))) then = val && val.then;
308+
if (isFunction(then)) {
309+
then.call(val, fns[0], fns[1], this.notify);
310+
} else {
311+
this.$$state.value = val;
312+
this.$$state.status = 1;
313+
scheduleProcessQueue(this.$$state);
314+
}
315+
} catch(e) {
316+
fns[1](e);
317+
exceptionHandler(e);
277318
}
319+
};
278320

279-
deferred = {
280-
promise: {
281-
then: function(onFulfilled, onRejected, progressBack) {
282-
var result = defer();
283-
284-
pending.push([result, onFulfilled, onRejected, progressBack]);
285-
if (status) scheduleProcessQueue();
286-
287-
return result.promise;
288-
},
289-
290-
"catch": function(callback) {
291-
return deferred.promise.then(null, callback);
292-
},
293-
294-
"finally": function(callback, progressBack) {
295-
function makePromise(value, resolved) {
296-
var result = defer();
297-
if (resolved) {
298-
result.resolve(value);
299-
} else {
300-
result.reject(value);
301-
}
302-
return result.promise;
303-
}
321+
Defer.prototype.reject = function(reason) {
322+
if (this.$$state.status) return;
323+
this.$$state.value = reason;
324+
this.$$state.status = 2;
325+
scheduleProcessQueue(this.$$state);
326+
};
304327

305-
function handleCallback(value, isResolved) {
306-
var callbackOutput = null;
307-
try {
308-
if (isFunction(callback)) callbackOutput = callback();
309-
} catch(e) {
310-
return makePromise(e, false);
311-
}
312-
if (callbackOutput && isFunction(callbackOutput.then)) {
313-
return callbackOutput.then(function() {
314-
return makePromise(value, isResolved);
315-
}, function(error) {
316-
return makePromise(error, false);
317-
});
318-
} else {
319-
return makePromise(value, isResolved);
320-
}
321-
}
328+
Defer.prototype.notify = function(progress) {
329+
var callbacks = this.$$state.pending;
322330

323-
return this.then(function(value) {
324-
return handleCallback(value, true);
325-
}, function(error) {
326-
return handleCallback(error, false);
327-
}, progressBack);
328-
}
329-
},
330-
resolve: resolve,
331-
reject: reject,
332-
notify: function(progress) {
333-
var callbacks = pending;
334-
335-
if (!status && callbacks.length) {
336-
nextTick(function() {
337-
var callback, result;
338-
for (var i = 0, ii = callbacks.length; i < ii; i++) {
339-
result = callbacks[i][0];
340-
callback = callbacks[i][3];
341-
try {
342-
result.notify(isFunction(callback) ? callback(progress) : progress);
343-
} catch(e) {
344-
exceptionHandler(e);
345-
}
346-
}
347-
});
331+
if (!this.$$state.status && callbacks.length) {
332+
nextTick(function() {
333+
var callback, result;
334+
for (var i = 0, ii = callbacks.length; i < ii; i++) {
335+
result = callbacks[i][0];
336+
callback = callbacks[i][3];
337+
try {
338+
result.notify(isFunction(callback) ? callback(progress) : progress);
339+
} catch(e) {
340+
exceptionHandler(e);
341+
}
348342
}
349-
}
350-
};
343+
});
344+
}
345+
};
346+
347+
/**
348+
* @ngdoc
349+
* @name ng.$q#defer
350+
* @methodOf ng.$q
351+
* @description
352+
* Creates a `Deferred` object which represents a task which will finish in the future.
353+
*
354+
* @returns {Deferred} Returns a new instance of deferred.
355+
*/
356+
var defer = function() {
357+
358+
var result = new Defer();
359+
result.resolve = bind(result, result.resolve);
360+
result.reject = bind(result, result.reject);
361+
result.notify = bind(result, result.notify);
362+
result.promise = new Promise(result.$$state);
351363

352-
return deferred;
364+
return result;
353365
};
354366

355367
/**

0 commit comments

Comments
 (0)