diff --git a/src/FirebaseArray.js b/src/FirebaseArray.js index dc4889e4..f67ddbfe 100644 --- a/src/FirebaseArray.js +++ b/src/FirebaseArray.js @@ -624,14 +624,13 @@ } var def = $firebaseUtils.defer(); - var batch = $firebaseUtils.batch(); - var created = batch(function(snap, prevChild) { + var created = $firebaseUtils.batch(function(snap, prevChild) { var rec = firebaseArray.$$added(snap, prevChild); if( rec ) { firebaseArray.$$process('child_added', rec, prevChild); } }); - var updated = batch(function(snap) { + var updated = $firebaseUtils.batch(function(snap) { var rec = firebaseArray.$getRecord($firebaseUtils.getKey(snap)); if( rec ) { var changed = firebaseArray.$$updated(snap); @@ -640,7 +639,7 @@ } } }); - var moved = batch(function(snap, prevChild) { + var moved = $firebaseUtils.batch(function(snap, prevChild) { var rec = firebaseArray.$getRecord($firebaseUtils.getKey(snap)); if( rec ) { var confirmed = firebaseArray.$$moved(snap, prevChild); @@ -649,7 +648,7 @@ } } }); - var removed = batch(function(snap) { + var removed = $firebaseUtils.batch(function(snap) { var rec = firebaseArray.$getRecord($firebaseUtils.getKey(snap)); if( rec ) { var confirmed = firebaseArray.$$removed(snap); @@ -660,11 +659,11 @@ }); var isResolved = false; - var error = batch(function(err) { + var error = $firebaseUtils.batch(function(err) { _initComplete(err); firebaseArray.$$error(err); }); - var initComplete = batch(_initComplete); + var initComplete = $firebaseUtils.batch(_initComplete); var sync = { destroy: destroy, diff --git a/src/FirebaseObject.js b/src/FirebaseObject.js index d6c84746..aa76171e 100644 --- a/src/FirebaseObject.js +++ b/src/FirebaseObject.js @@ -433,8 +433,7 @@ var isResolved = false; var def = $firebaseUtils.defer(); - var batch = $firebaseUtils.batch(); - var applyUpdate = batch(function(snap) { + var applyUpdate = $firebaseUtils.batch(function(snap) { var changed = firebaseObject.$$updated(snap); if( changed ) { // notifies $watch listeners and @@ -442,8 +441,8 @@ firebaseObject.$$notify(); } }); - var error = batch(firebaseObject.$$error, firebaseObject); - var initComplete = batch(_initComplete); + var error = $firebaseUtils.batch(firebaseObject.$$error, firebaseObject); + var initComplete = $firebaseUtils.batch(_initComplete); var sync = { isDestroyed: false, diff --git a/src/module.js b/src/module.js index 460de19a..1bff256b 100644 --- a/src/module.js +++ b/src/module.js @@ -5,15 +5,6 @@ // services will live. angular.module("firebase", []) //todo use $window - .value("Firebase", exports.Firebase) - - // used in conjunction with firebaseUtils.debounce function, this is the - // amount of time we will wait for additional records before triggering - // Angular's digest scope to dirty check and re-render DOM elements. A - // larger number here significantly improves performance when working with - // big data sets that are frequently changing in the DOM, but delays the - // speed at which each record is rendered in real-time. A number less than - // 100ms will usually be optimal. - .value('firebaseBatchDelay', 50 /* milliseconds */); + .value("Firebase", exports.Firebase); })(window); \ No newline at end of file diff --git a/src/utils.js b/src/utils.js index 4263409c..a4490e42 100644 --- a/src/utils.js +++ b/src/utils.js @@ -23,8 +23,8 @@ } ]) - .factory('$firebaseUtils', ["$q", "$timeout", "firebaseBatchDelay", - function($q, $timeout, firebaseBatchDelay) { + .factory('$firebaseUtils', ["$q", "$timeout", "$rootScope", + function($q, $timeout, $rootScope) { // ES6 style promises polyfill for angular 1.2.x // 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 @@ var utils = { /** - * Returns a function which, each time it is invoked, will pause for `wait` - * milliseconds before invoking the original `fn` instance. If another - * request is received in that time, it resets `wait` up until `maxWait` is - * reached. + * Returns a function which, each time it is invoked, will gather up the values until + * the next "tick" in the Angular compiler process. Then they are all run at the same + * time to avoid multiple cycles of the digest loop. Internally, this is done using $evalAsync() * - * Unlike a debounce function, once wait is received, all items that have been - * queued will be invoked (not just once per execution). It is acceptable to use 0, - * which means to batch all synchronously queued items. - * - * The batch function actually returns a wrap function that should be called on each - * method that is to be batched. - * - *

-           *   var total = 0;
-           *   var batchWrapper = batch(10, 100);
-           *   var fn1 = batchWrapper(function(x) { return total += x; });
-           *   var fn2 = batchWrapper(function() { console.log(total); });
-           *   fn1(10);
-           *   fn2();
-           *   fn1(10);
-           *   fn2();
-           *   console.log(total); // 0 (nothing invoked yet)
-           *   // after 10ms will log "10" and then "20"
-           * 
- * - * @param {int} wait number of milliseconds to pause before sending out after each invocation - * @param {int} maxWait max milliseconds to wait before sending out, defaults to wait * 10 or 100 + * @param {Function} action + * @param {Object} [context] * @returns {Function} */ - batch: function(wait, maxWait) { - wait = typeof('wait') === 'number'? wait : firebaseBatchDelay; - if( !maxWait ) { maxWait = wait*10 || 100; } - var queue = []; - var start; - var cancelTimer; - var runScheduledForNextTick; - - // returns `fn` wrapped in a function that queues up each call event to be - // invoked later inside fo runNow() - function createBatchFn(fn, context) { - if( typeof(fn) !== 'function' ) { - throw new Error('Must provide a function to be batched. Got '+fn); - } - return function() { - var args = Array.prototype.slice.call(arguments, 0); - queue.push([fn, context, args]); - resetTimer(); - }; - } - - // clears the current wait timer and creates a new one - // however, if maxWait is exceeded, calls runNow() on the next tick. - function resetTimer() { - if( cancelTimer ) { - cancelTimer(); - cancelTimer = null; - } - if( start && Date.now() - start > maxWait ) { - if(!runScheduledForNextTick){ - runScheduledForNextTick = true; - utils.compile(runNow); - } - } - else { - if( !start ) { start = Date.now(); } - cancelTimer = utils.wait(runNow, wait); - } - } - - // Clears the queue and invokes all of the functions awaiting notification - function runNow() { - cancelTimer = null; - start = null; - runScheduledForNextTick = false; - var copyList = queue.slice(0); - queue = []; - angular.forEach(copyList, function(parts) { - parts[0].apply(parts[1], parts[2]); + batch: function(action, context) { + return function() { + var args = Array.prototype.slice.call(arguments, 0); + $rootScope.$evalAsync(function() { + action.apply(context, args); }); - } - - return createBatchFn; + }; }, /** @@ -492,7 +425,6 @@ */ VERSION: '0.0.0', - batchDelay: firebaseBatchDelay, allPromises: $q.all.bind($q) }; diff --git a/tests/unit/utils.spec.js b/tests/unit/utils.spec.js index a55acf80..74a7e62f 100644 --- a/tests/unit/utils.spec.js +++ b/tests/unit/utils.spec.js @@ -46,8 +46,7 @@ describe('$firebaseUtils', function () { it('should trigger function with arguments', function() { var spy = jasmine.createSpy(); - var batch = $utils.batch(); - var b = batch(spy); + var b = $utils.batch(spy); b('foo', 'bar'); $timeout.flush(); expect(spy).toHaveBeenCalledWith('foo', 'bar'); @@ -55,8 +54,7 @@ describe('$firebaseUtils', function () { it('should queue up requests until timeout', function() { var spy = jasmine.createSpy(); - var batch = $utils.batch(); - var b = batch(spy); + var b = $utils.batch(spy); for(var i=0; i < 4; i++) { b(i); } @@ -70,8 +68,7 @@ describe('$firebaseUtils', function () { var spy = jasmine.createSpy().and.callFake(function() { b = this; }); - var batch = $utils.batch(); - batch(spy, a)(); + $utils.batch(spy, a)(); $timeout.flush(); expect(spy).toHaveBeenCalled(); expect(b).toBe(a);