diff options
author | Wilson Page <wilsonpage@me.com> | 2013-09-17 03:03:19 -0700 |
---|---|---|
committer | Wilson Page <wilsonpage@me.com> | 2013-09-17 03:03:19 -0700 |
commit | 70e0e69c155b00a41350ebd40e83e8b82a15f42a (patch) | |
tree | ad799a772e84f5a14a6489d60b47736ec534cdb0 | |
parent | ea5ec64119d5f8ac07180491b9043a361d66ceb0 (diff) | |
parent | 70e85478eb9df9b27f7a3f418b32c4f4f64ad157 (diff) | |
download | fastdom-70e0e69c155b00a41350ebd40e83e8b82a15f42a.zip fastdom-70e0e69c155b00a41350ebd40e83e8b82a15f42a.tar.gz fastdom-70e0e69c155b00a41350ebd40e83e8b82a15f42a.tar.bz2 |
Merge pull request #8 from wilsonpage/dev
Change the way callback context is stored and scheduled jobs are cleared (closes #7)
-rw-r--r-- | bower.json | 2 | ||||
-rw-r--r-- | component.json | 2 | ||||
-rw-r--r-- | lib/fastdom.js | 132 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | test/setup.js | 8 | ||||
-rw-r--r-- | test/test.clear.js | 16 | ||||
-rw-r--r-- | test/test.set.js | 37 |
7 files changed, 129 insertions, 70 deletions
@@ -1,7 +1,7 @@ { "name": "fastdom", "description": "Eliminates layout thrashing by batching DOM read/write operations", - "version": "0.4.2", + "version": "0.5.0", "main": "lib/fastdom.js", "scripts": [ "lib/fastdom.js" diff --git a/component.json b/component.json index 56ebe0f..c73a26f 100644 --- a/component.json +++ b/component.json @@ -1,7 +1,7 @@ { "name": "fastdom", "description": "Eliminates layout thrashing by batching DOM read/write operations", - "version": "0.4.2", + "version": "0.5.0", "main": "lib/fastdom.js", "scripts": [ "lib/fastdom.js" diff --git a/lib/fastdom.js b/lib/fastdom.js index 6cba5c0..60b38d8 100644 --- a/lib/fastdom.js +++ b/lib/fastdom.js @@ -33,10 +33,12 @@ * @constructor */ function FastDom() { - this.reads = []; - this.writes = []; + this.lastId = 0; + this.jobs = {}; this.mode = null; this.pending = false; + this.reads = []; + this.writes = []; } /** @@ -47,8 +49,9 @@ * @api public */ FastDom.prototype.read = function(fn, ctx) { - add(this.reads, fn, ctx); - this.request('read'); + var id = this._add(this.reads, fn, ctx); + this._request('read'); + return id; }; /** @@ -59,30 +62,31 @@ * @api public */ FastDom.prototype.write = function(fn, ctx) { - add(this.writes, fn, ctx); - this.request('write'); + var id = this._add(this.writes, fn, ctx); + this._request('write'); + return id; }; /** * Removes a job from * the 'reads' queue. * - * @param {Function} fn + * @param {Number} id * @api public */ - FastDom.prototype.clearRead = function(fn) { - remove(this.reads, fn); + FastDom.prototype.clearRead = function(id) { + this._remove(this.reads, id); }; /** * Removes a job from * the 'writes' queue. * - * @param {Function} fn + * @param {Number} id * @api public */ - FastDom.prototype.clearWrite = function(fn) { - remove(this.writes, fn); + FastDom.prototype.clearWrite = function(id) { + this._remove(this.writes, id); }; /** @@ -93,31 +97,32 @@ * @param {String} type * @api private */ - FastDom.prototype.request = function(type) { + FastDom.prototype._request = function(type) { + var mode = this.mode; var self = this; // If we are currently writing, we don't // need to scedule a new frame as this // job will be emptied from the write queue - if (this.mode === 'writing' && type === 'write') return; + if (mode === 'writing' && type === 'write') return; // If we are reading we don't need to schedule // a new frame as this read will be emptied // in the currently active read queue - if (this.mode === 'reading' && type === 'read') return; + if (mode === 'reading' && type === 'read') return; // If we are reading we don't need to schedule // a new frame and this write job will be run // after the read queue has been emptied in the // currently active frame. - if (this.mode === 'reading' && type === 'write') return; + if (mode === 'reading' && type === 'write') return; // If there is already a frame // scheduled, don't schedule another one if (this.pending) return; // Schedule frame (preserving context) - raf(function() { self.frame(); }); + raf(function() { self._frame(); }); // Set flag to indicate // a frame has been scheduled @@ -125,6 +130,16 @@ }; /** + * Generates a unique + * id for a job. + * + * @return {Number} + */ + FastDom.prototype._uniqueId = function() { + return ++this.lastId; + }; + + /** * Calls each job in * the list passed. * @@ -136,18 +151,16 @@ * @param {Array} list * @api private */ - FastDom.prototype.run = function(list) { - var fn; + FastDom.prototype._run = function(list) { var ctx; + var job; + var id; - while (list.length) { - fn = list.shift(); - ctx = fn._dbctx || this; - try { - fn.call(ctx); - } catch (err) { - // TODO: console.error if options.silent === false. - } + while (id = list.shift()) { + job = this.jobs[id]; + ctx = job.ctx || this; + delete this.jobs[id]; + try { job.fn.call(ctx); } catch (e) {} } }; @@ -157,7 +170,7 @@ * * @api private */ - FastDom.prototype.frame = function() { + FastDom.prototype._frame = function() { // Set the pending flag to // false so that any new requests @@ -167,12 +180,12 @@ // Set the mode to 'reading', // then empty all read jobs this.mode = 'reading'; - this.run(this.reads); + this._run(this.reads); // Set the mode to 'writing' // then empty all write jobs this.mode = 'writing'; - this.run(this.writes); + this._run(this.writes); this.mode = null; }; @@ -182,8 +195,6 @@ * by the number of frames * specified. * - * TODO: - * * @param {Function} fn * @param {Number} frames * @api public @@ -191,7 +202,7 @@ FastDom.prototype.defer = function(fn, frames) { (function wrapped() { if (frames-- === 0) { - try { fn(); } catch (e){} + try { fn(); } catch (e) {} } else { raf(wrapped); } @@ -199,40 +210,45 @@ }; /** - * Util - */ - - /** - * Adds a function to - * the given array. + * Adds a new job to + * the given queue. * - * If a context is given - * it is stored on the - * function object for - * later. - * - * @param {Array} array + * @param {Array} list * @param {Function} fn * @param {Object} ctx + * @returns {Number} id * @api private */ - function add(array, fn, ctx) { - if (ctx) fn._dbctx = ctx; - array.push(fn); - } + FastDom.prototype._add = function(list, fn, ctx) { + var id = this._uniqueId(); + + // Store this job + this.jobs[id] = { + fn: fn, + ctx: ctx + }; + + // Push the id of + // this job into + // the given queue + list.push(id); + + // Return the id + return id; + }; /** - * Removes a function - * from the given array. - * - * @param {Array} array - * @param {Function} item + * Removes a job from + * the given queue. + * @param {Array} list + * @param {Number} id * @api private */ - function remove(array, fn) { - var index = array.indexOf(fn); - if (~index) array.splice(index, 1); - } + FastDom.prototype._remove = function(list, id) { + var index = list.indexOf(id); + if (~index) list.splice(index, 1); + delete this.jobs[id]; + }; /** * Expose 'FastDom' diff --git a/package.json b/package.json index f7a28a5..5022496 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "fastdom", "description": "Eliminates layout thrashing by batching DOM read/write operations", - "version": "0.4.2", + "version": "0.5.0", "main": "lib/fastdom.js", "scripts": { "test": "./node_modules/.bin/mocha-phantomjs test/index.html" diff --git a/test/setup.js b/test/setup.js index bfe48f1..0844695 100644 --- a/test/setup.js +++ b/test/setup.js @@ -9,4 +9,10 @@ var raf = window.requestAnimationFrame var FastDom = fastdom.constructor; // Alias chai.assert -var assert = chai.assert;
\ No newline at end of file +var assert = chai.assert; + +function objectLength(object) { + var l = 0; + for (var key in object) l++; + return l; +}
\ No newline at end of file diff --git a/test/test.clear.js b/test/test.clear.js index be273a4..6b274fe 100644 --- a/test/test.clear.js +++ b/test/test.clear.js @@ -5,8 +5,8 @@ suite('Clear', function(){ var fastdom = new FastDom(); var read = sinon.spy(); - fastdom.read(read); - fastdom.clearRead(read); + var id = fastdom.read(read); + fastdom.clearRead(id); raf(function() { assert(!read.called); @@ -19,8 +19,8 @@ suite('Clear', function(){ var read = sinon.spy(); var read2 = sinon.spy(); - fastdom.read(read); - fastdom.clearRead(read2); + var id = fastdom.read(read); + fastdom.clearRead(id); raf(function() { assert(!read2.called); @@ -33,9 +33,9 @@ suite('Clear', function(){ var read = sinon.spy(); var write = sinon.spy(); - fastdom.write(write); + var id = fastdom.write(write); fastdom.read(function() { - fastdom.clearWrite(write); + fastdom.clearWrite(id); raf(function() { assert(!read.called); @@ -47,9 +47,9 @@ suite('Clear', function(){ test("Should not run 'write' job if cleared", function(done) { var fastdom = new FastDom(); var write = sinon.spy(); + var id = fastdom.write(write); - fastdom.write(write); - fastdom.clearWrite(write); + fastdom.clearWrite(id); raf(function() { assert(!write.called); diff --git a/test/test.set.js b/test/test.set.js index c8d9989..62f7e5f 100644 --- a/test/test.set.js +++ b/test/test.set.js @@ -124,4 +124,41 @@ suite('Set', function() { done(); }, ctx); }); + + test("Should have empty job hash when batch complete", function(done) { + var fastdom = new FastDom(); + + fastdom.read(function(){}); + fastdom.read(function(){}); + fastdom.write(function(){}); + fastdom.write(function(){}); + + // Check there are four jobs stored + assert.equal(objectLength(fastdom.jobs), 4); + + raf(function() { + assert.equal(objectLength(fastdom.jobs), 0); + done(); + }); + }); + + test("Should maintain correct context if single method is registered twice", function(done) { + var fastdom = new FastDom(); + var ctx1 = { foo: 'bar' }; + var ctx2 = { bar: 'baz' }; + + function shared(){} + + var spy1 = sinon.spy(shared); + var spy2 = sinon.spy(shared); + + fastdom.read(spy1, ctx1); + fastdom.read(spy2, ctx2); + + raf(function() { + spy1.calledOn(ctx1); + spy2.calledOn(ctx2); + done(); + }); + }); });
\ No newline at end of file |