Skip to content
This repository was archived by the owner on Mar 17, 2025. It is now read-only.

Commit b7be0b8

Browse files
committed
Fixes #411 - switch to $evalAsync() for improved performance
1 parent 8cec2e9 commit b7be0b8

File tree

5 files changed

+26
-108
lines changed

5 files changed

+26
-108
lines changed

src/FirebaseArray.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -624,14 +624,13 @@
624624
}
625625

626626
var def = $firebaseUtils.defer();
627-
var batch = $firebaseUtils.batch();
628-
var created = batch(function(snap, prevChild) {
627+
var created = $firebaseUtils.batch(function(snap, prevChild) {
629628
var rec = firebaseArray.$$added(snap, prevChild);
630629
if( rec ) {
631630
firebaseArray.$$process('child_added', rec, prevChild);
632631
}
633632
});
634-
var updated = batch(function(snap) {
633+
var updated = $firebaseUtils.batch(function(snap) {
635634
var rec = firebaseArray.$getRecord($firebaseUtils.getKey(snap));
636635
if( rec ) {
637636
var changed = firebaseArray.$$updated(snap);
@@ -640,7 +639,7 @@
640639
}
641640
}
642641
});
643-
var moved = batch(function(snap, prevChild) {
642+
var moved = $firebaseUtils.batch(function(snap, prevChild) {
644643
var rec = firebaseArray.$getRecord($firebaseUtils.getKey(snap));
645644
if( rec ) {
646645
var confirmed = firebaseArray.$$moved(snap, prevChild);
@@ -649,7 +648,7 @@
649648
}
650649
}
651650
});
652-
var removed = batch(function(snap) {
651+
var removed = $firebaseUtils.batch(function(snap) {
653652
var rec = firebaseArray.$getRecord($firebaseUtils.getKey(snap));
654653
if( rec ) {
655654
var confirmed = firebaseArray.$$removed(snap);
@@ -660,11 +659,11 @@
660659
});
661660

662661
var isResolved = false;
663-
var error = batch(function(err) {
662+
var error = $firebaseUtils.batch(function(err) {
664663
_initComplete(err);
665664
firebaseArray.$$error(err);
666665
});
667-
var initComplete = batch(_initComplete);
666+
var initComplete = $firebaseUtils.batch(_initComplete);
668667

669668
var sync = {
670669
destroy: destroy,

src/FirebaseObject.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -433,17 +433,16 @@
433433

434434
var isResolved = false;
435435
var def = $firebaseUtils.defer();
436-
var batch = $firebaseUtils.batch();
437-
var applyUpdate = batch(function(snap) {
436+
var applyUpdate = $firebaseUtils.batch(function(snap) {
438437
var changed = firebaseObject.$$updated(snap);
439438
if( changed ) {
440439
// notifies $watch listeners and
441440
// updates $scope if bound to a variable
442441
firebaseObject.$$notify();
443442
}
444443
});
445-
var error = batch(firebaseObject.$$error, firebaseObject);
446-
var initComplete = batch(_initComplete);
444+
var error = $firebaseUtils.batch(firebaseObject.$$error, firebaseObject);
445+
var initComplete = $firebaseUtils.batch(_initComplete);
447446

448447
var sync = {
449448
isDestroyed: false,

src/module.js

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,6 @@
55
// services will live.
66
angular.module("firebase", [])
77
//todo use $window
8-
.value("Firebase", exports.Firebase)
9-
10-
// used in conjunction with firebaseUtils.debounce function, this is the
11-
// amount of time we will wait for additional records before triggering
12-
// Angular's digest scope to dirty check and re-render DOM elements. A
13-
// larger number here significantly improves performance when working with
14-
// big data sets that are frequently changing in the DOM, but delays the
15-
// speed at which each record is rendered in real-time. A number less than
16-
// 100ms will usually be optimal.
17-
.value('firebaseBatchDelay', 50 /* milliseconds */);
8+
.value("Firebase", exports.Firebase);
189

1910
})(window);

src/utils.js

Lines changed: 13 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
}
2424
])
2525

26-
.factory('$firebaseUtils', ["$q", "$timeout", "firebaseBatchDelay",
27-
function($q, $timeout, firebaseBatchDelay) {
26+
.factory('$firebaseUtils', ["$q", "$timeout", "$rootScope",
27+
function($q, $timeout, $rootScope) {
2828

2929
// ES6 style promises polyfill for angular 1.2.x
3030
// Copied from angular 1.3.x implementation: https://github.com/angular/angular.js/blob/v1.3.5/src/ng/q.js#L539
@@ -50,88 +50,21 @@
5050

5151
var utils = {
5252
/**
53-
* Returns a function which, each time it is invoked, will pause for `wait`
54-
* milliseconds before invoking the original `fn` instance. If another
55-
* request is received in that time, it resets `wait` up until `maxWait` is
56-
* reached.
53+
* Returns a function which, each time it is invoked, will gather up the values until
54+
* the next "tick" in the Angular compiler process. Then they are all run at the same
55+
* time to avoid multiple cycles of the digest loop. Internally, this is done using $evalAsync()
5756
*
58-
* Unlike a debounce function, once wait is received, all items that have been
59-
* queued will be invoked (not just once per execution). It is acceptable to use 0,
60-
* which means to batch all synchronously queued items.
61-
*
62-
* The batch function actually returns a wrap function that should be called on each
63-
* method that is to be batched.
64-
*
65-
* <pre><code>
66-
* var total = 0;
67-
* var batchWrapper = batch(10, 100);
68-
* var fn1 = batchWrapper(function(x) { return total += x; });
69-
* var fn2 = batchWrapper(function() { console.log(total); });
70-
* fn1(10);
71-
* fn2();
72-
* fn1(10);
73-
* fn2();
74-
* console.log(total); // 0 (nothing invoked yet)
75-
* // after 10ms will log "10" and then "20"
76-
* </code></pre>
77-
*
78-
* @param {int} wait number of milliseconds to pause before sending out after each invocation
79-
* @param {int} maxWait max milliseconds to wait before sending out, defaults to wait * 10 or 100
57+
* @param {Function} action
58+
* @param {Object} [context]
8059
* @returns {Function}
8160
*/
82-
batch: function(wait, maxWait) {
83-
wait = typeof('wait') === 'number'? wait : firebaseBatchDelay;
84-
if( !maxWait ) { maxWait = wait*10 || 100; }
85-
var queue = [];
86-
var start;
87-
var cancelTimer;
88-
var runScheduledForNextTick;
89-
90-
// returns `fn` wrapped in a function that queues up each call event to be
91-
// invoked later inside fo runNow()
92-
function createBatchFn(fn, context) {
93-
if( typeof(fn) !== 'function' ) {
94-
throw new Error('Must provide a function to be batched. Got '+fn);
95-
}
96-
return function() {
97-
var args = Array.prototype.slice.call(arguments, 0);
98-
queue.push([fn, context, args]);
99-
resetTimer();
100-
};
101-
}
102-
103-
// clears the current wait timer and creates a new one
104-
// however, if maxWait is exceeded, calls runNow() on the next tick.
105-
function resetTimer() {
106-
if( cancelTimer ) {
107-
cancelTimer();
108-
cancelTimer = null;
109-
}
110-
if( start && Date.now() - start > maxWait ) {
111-
if(!runScheduledForNextTick){
112-
runScheduledForNextTick = true;
113-
utils.compile(runNow);
114-
}
115-
}
116-
else {
117-
if( !start ) { start = Date.now(); }
118-
cancelTimer = utils.wait(runNow, wait);
119-
}
120-
}
121-
122-
// Clears the queue and invokes all of the functions awaiting notification
123-
function runNow() {
124-
cancelTimer = null;
125-
start = null;
126-
runScheduledForNextTick = false;
127-
var copyList = queue.slice(0);
128-
queue = [];
129-
angular.forEach(copyList, function(parts) {
130-
parts[0].apply(parts[1], parts[2]);
61+
batch: function(action, context) {
62+
return function() {
63+
var args = Array.prototype.slice.call(arguments, 0);
64+
$rootScope.$evalAsync(function() {
65+
action.apply(context, args);
13166
});
132-
}
133-
134-
return createBatchFn;
67+
};
13568
},
13669

13770
/**
@@ -492,7 +425,6 @@
492425
*/
493426
VERSION: '0.0.0',
494427

495-
batchDelay: firebaseBatchDelay,
496428
allPromises: $q.all.bind($q)
497429
};
498430

tests/unit/utils.spec.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,15 @@ describe('$firebaseUtils', function () {
4646

4747
it('should trigger function with arguments', function() {
4848
var spy = jasmine.createSpy();
49-
var batch = $utils.batch();
50-
var b = batch(spy);
49+
var b = $utils.batch(spy);
5150
b('foo', 'bar');
5251
$timeout.flush();
5352
expect(spy).toHaveBeenCalledWith('foo', 'bar');
5453
});
5554

5655
it('should queue up requests until timeout', function() {
5756
var spy = jasmine.createSpy();
58-
var batch = $utils.batch();
59-
var b = batch(spy);
57+
var b = $utils.batch(spy);
6058
for(var i=0; i < 4; i++) {
6159
b(i);
6260
}
@@ -70,8 +68,7 @@ describe('$firebaseUtils', function () {
7068
var spy = jasmine.createSpy().and.callFake(function() {
7169
b = this;
7270
});
73-
var batch = $utils.batch();
74-
batch(spy, a)();
71+
$utils.batch(spy, a)();
7572
$timeout.flush();
7673
expect(spy).toHaveBeenCalled();
7774
expect(b).toBe(a);

0 commit comments

Comments
 (0)