Просмотр исходного кода

Render queue behavior changes, entity support

Adam Shaw 8 лет назад
Родитель
Сommit
cecdf80dbf
3 измененных файлов с 93 добавлено и 149 удалено
  1. 24 78
      src/common/RenderQueue.js
  2. 2 2
      src/component/DateComponent.js
  3. 67 69
      tests/util/RenderQueue.js

+ 24 - 78
src/common/RenderQueue.js

@@ -14,16 +14,20 @@ var RenderQueue = TaskQueue.extend({
 	},
 
 
-	queue: function(namespace, type, taskFunc) {
+	/*
+	all args are required
+	*/
+	queue: function(entityId, namespace, type, taskFunc) {
 
 		if (this.isKilled) {
 			return;
 		}
 
 		var task = {
-			func: taskFunc,
+			entityId: entityId,
 			namespace: namespace,
-			type: type
+			type: type,
+			func: taskFunc
 		};
 		var waitMs;
 
@@ -35,10 +39,6 @@ var RenderQueue = TaskQueue.extend({
 			if (namespace === this.waitNamespace && waitMs != null) {
 				this.delayWait(waitMs);
 			}
-			else {
-				this.clearWait();
-				this.tryStart();
-			}
 		}
 
 		if (this.compoundTask(task)) { // appended to queue?
@@ -87,36 +87,10 @@ var RenderQueue = TaskQueue.extend({
 	},
 
 
-	clearWait: function() {
-		if (this.waitNamespace) {
-			clearTimeout(this.waitId);
-			this.waitId = null;
-			this.waitNamespace = null;
-		}
-	},
-
-
 	canRunNext: function() {
-		if (!TaskQueue.prototype.canRunNext.apply(this, arguments)) {
-			return false;
-		}
-
-		// waiting for a certain namespace to stop receiving tasks?
-		if (this.waitNamespace) {
-
-			// if there was a different namespace task in the meantime,
-			// that forces all previously-waiting tasks to suddenly execute.
-			// TODO: find a way to do this in constant time.
-			for (var q = this.q, i = 0; i < q.length; i++) {
-				if (q[i].namespace !== this.waitNamespace) {
-					return true; // allow execution
-				}
-			}
-
-			return false;
-		}
-
-		return true;
+		return TaskQueue.prototype.canRunNext.apply(this, arguments) &&
+			!this.isKilled &&
+			(!this.waitNamespace || this.q[0].namespace !== this.waitNamespace);
 	},
 
 
@@ -130,53 +104,25 @@ var RenderQueue = TaskQueue.extend({
 		var shouldAppend = true;
 		var i, task;
 
-		if (newTask.namespace) {
-
-			if (newTask.type === 'destroy' || newTask.type === 'init') {
+		if (newTask.type === 'destroy') {
 
-				// remove all add/remove ops with same namespace, regardless of order
-				for (i = q.length - 1; i >= 0; i--) {
-					task = q[i];
+			// remove ops with same entityId and namespace
+			for (i = q.length - 1; i >= 0; i--) {
+				task = q[i];
 
-					if (
-						task.namespace === newTask.namespace &&
-						(task.type === 'add' || task.type === 'remove')
-					) {
-						q.splice(i, 1); // remove task
+				if (
+					task.entityId === newTask.entityId &&
+					task.namespace === newTask.namespace
+				) {
+					if (task.type === 'init') { // cancels out the destroy
+						shouldAppend = false;
 					}
-				}
 
-				if (newTask.type === 'destroy') {
-					// eat away final init/destroy operation
-					if (q.length) {
-						task = q[q.length - 1]; // last task
-
-						if (task.namespace === newTask.namespace) {
-
-							// the init and our destroy cancel each other out
-							if (task.type === 'init') {
-								shouldAppend = false;
-								q.pop();
-							}
-							// prefer to use the destroy operation that's already present
-							else if (task.type === 'destroy') {
-								shouldAppend = false;
-							}
-						}
+					if (task.type === 'destroy-trigger' && shouldAppend) {
+						; // a destroy will still happen, so keep this task
 					}
-				}
-				else if (newTask.type === 'init') {
-					// eat away final init operation
-					if (q.length) {
-						task = q[q.length - 1]; // last task
-
-						if (
-							task.namespace === newTask.namespace &&
-							task.type === 'init'
-						) {
-							// our init operation takes precedence
-							q.pop();
-						}
+					else {
+						q.splice(i, 1); // remove task
 					}
 				}
 			}

+ 2 - 2
src/component/DateComponent.js

@@ -22,7 +22,7 @@ var DateComponent = Component.extend({
 	constructor: function() {
 		Component.call(this);
 
-		this.uid = DateComponent.guid++;
+		this.uid = String(DateComponent.guid++);
 		this.children = [];
 
 		this.nextDayThreshold = moment.duration(this.opt('nextDayThreshold'));
@@ -54,7 +54,7 @@ var DateComponent = Component.extend({
 	requestRender: function(namespace, type, method, args) {
 		var _this = this;
 
-		this._getView().calendar.renderQueue.queue(this.uid + ':' + namespace, type, function() {
+		this._getView().calendar.renderQueue.queue(this.uid, namespace, type, function() {
 			method.apply(_this, args);
 		});
 	},

+ 67 - 69
tests/util/RenderQueue.js

@@ -6,19 +6,19 @@ describe('RenderQueue', function() {
 		var ops = [];
 		var q = new RenderQueue();
 
-		q.queue('foo', 'init', function() {
+		q.queue('ent0', 'foo', 'init', function() {
 			ops.push('fooinit');
 		});
 
-		q.queue('foo', 'add', function() {
+		q.queue('ent0', 'foo', 'add', function() {
 			ops.push('fooremove');
 		});
 
-		q.queue('foo', 'remove', function() {
+		q.queue('ent0', 'foo', 'remove', function() {
 			ops.push('fooadd');
 		});
 
-		q.queue('foo', 'destroy', function() {
+		q.queue('ent0', 'foo', 'destroy', function() {
 			ops.push('foodestroy');
 		});
 
@@ -29,20 +29,20 @@ describe('RenderQueue', function() {
 
 		describe('using clear action', function() {
 
-			it('destroys add/remove operations in same namespace', function() {
+			it('destroys add/remove operations in same entity+namespace', function() {
 				var ops = [];
 				var q = new RenderQueue();
 				q.pause();
 
-				q.queue('foo', 'add', function() {
+				q.queue('ent0', 'foo', 'add', function() {
 					ops.push('fooadd');
 				});
 
-				q.queue('foo', 'remove', function() {
+				q.queue('ent0', 'foo', 'remove', function() {
 					ops.push('fooremove');
 				});
 
-				q.queue('foo', 'destroy', function() {
+				q.queue('ent0', 'foo', 'destroy', function() {
 					ops.push('foodestroy');
 				});
 
@@ -51,28 +51,58 @@ describe('RenderQueue', function() {
 				expect(ops).toEqual([ 'foodestroy' ]);
 			});
 
-			it('is cancelled out by an init in same namespace', function() {
+			fit('destroys add/remove operations in same entity+namespace, keeping other entities', function() {
 				var ops = [];
 				var q = new RenderQueue();
 				q.pause();
 
-				q.queue('bar', 'init', function() {
+				q.queue('ent0', 'foo', 'add', function() {
+					ops.push('foo0add');
+				});
+
+				q.queue('ent1', 'foo', 'add', function() {
+					ops.push('foo1add');
+				});
+
+				q.queue('ent0', 'foo', 'remove', function() {
+					ops.push('foo0remove');
+				});
+
+				q.queue('ent1', 'foo', 'remove', function() {
+					ops.push('foo1remove');
+				});
+
+				q.queue('ent0', 'foo', 'destroy', function() {
+					ops.push('foo0destroy');
+				});
+
+				expect(ops).toEqual([]);
+				q.resume();
+				expect(ops).toEqual([ 'foo1add', 'foo1remove', 'foo0destroy' ]);
+			});
+
+			it('is cancelled out by an init in same entity+namespace', function() {
+				var ops = [];
+				var q = new RenderQueue();
+				q.pause();
+
+				q.queue('ent0', 'bar', 'init', function() {
 					ops.push('barinit');
 				});
 
-				q.queue('foo', 'init', function() {
+				q.queue('ent0', 'foo', 'init', function() {
 					ops.push('fooinit');
 				});
 
-				q.queue('foo', 'add', function() {
+				q.queue('ent0', 'foo', 'add', function() {
 					ops.push('fooadd');
 				});
 
-				q.queue('foo', 'remove', function() {
+				q.queue('ent0', 'foo', 'remove', function() {
 					ops.push('fooadd');
 				});
 
-				q.queue('foo', 'destroy', function() {
+				q.queue('ent0', 'foo', 'destroy', function() {
 					ops.push('fooadd');
 				});
 
@@ -91,11 +121,11 @@ describe('RenderQueue', function() {
 				foo: 100
 			});
 
-			q.queue('foo', 'init', function() {
+			q.queue('ent0', 'foo', 'init', function() {
 				ops.push('fooinit');
 			});
 
-			q.queue('foo', 'add', function() {
+			q.queue('ent0', 'foo', 'add', function() {
 				ops.push('fooadd');
 			});
 
@@ -113,12 +143,12 @@ describe('RenderQueue', function() {
 				foo: 100
 			});
 
-			q.queue('foo', 'init', function() {
+			q.queue('ent0', 'foo', 'init', function() {
 				ops.push('fooinit');
 			});
 
  			setTimeout(function() {
-				q.queue('foo', 'add', function() {
+				q.queue('ent0', 'foo', 'add', function() {
 					ops.push('fooadd');
 				});
 			}, 50);
@@ -133,56 +163,61 @@ describe('RenderQueue', function() {
 			}, 175);
 		});
 
-		it('synchronously executes queue when sync non-namespace operation happens', function() {
+		it('causes waiting tasks to delay subsequent non-waiting tasks', function(done) {
 			var ops = [];
 			var q = new RenderQueue({
 				foo: 100
 			});
 
-			q.queue('foo', 'init', function() {
+			q.queue('ent0', 'foo', 'init', function() {
 				ops.push('fooinit');
 			});
 
-			q.queue('foo', 'add', function() {
+			q.queue('ent0', 'foo', 'add', function() {
 				ops.push('fooadd');
 			});
 
 			expect(ops).toEqual([]);
 
-			q.queue('bar', 'init', function() {
+			q.queue('ent0', 'bar', 'init', function() {
 				ops.push('barinit');
 			});
 
-			expect(ops).toEqual([ 'fooinit', 'fooadd', 'barinit' ]);
+			expect(ops).toEqual([]);
+
+			setTimeout(function() {
+				expect(ops).toEqual([ 'fooinit', 'fooadd', 'barinit' ]);
+				done();
+			}, 125);
 		});
 
-		it('synchronously executes queue when async non-namespace operation happens', function(done) {
+		it('causes waiting tasks to ignore subsequent waiting tasks\' timeouts', function(done) {
 			var ops = [];
 			var q = new RenderQueue({
 				foo: 100,
-				bar: 100
+				bar: 1000
 			});
 
-			q.queue('foo', 'init', function() {
+			q.queue('ent0', 'foo', 'init', function() {
 				ops.push('fooinit');
 			});
 
-			q.queue('foo', 'add', function() {
+			q.queue('ent0', 'foo', 'add', function() {
 				ops.push('fooadd');
 			});
 
 			expect(ops).toEqual([]);
 
-			q.queue('bar', 'init', function() {
+			q.queue('ent0', 'bar', 'init', function() {
 				ops.push('barinit');
 			});
 
-			expect(ops).toEqual([ 'fooinit', 'fooadd' ]);
+			expect(ops).toEqual([]);
 
 			setTimeout(function() {
 				expect(ops).toEqual([ 'fooinit', 'fooadd', 'barinit' ]);
 				done();
-			}, 200);
+			}, 125);
 		});
 
 		it('resumes non-waiting tasks when unpaused', function(done) {
@@ -193,11 +228,11 @@ describe('RenderQueue', function() {
 
 			q.pause();
 
-			q.queue('bar', 'init', function() {
+			q.queue('ent0', 'bar', 'init', function() {
 				ops.push('barinit');
 			});
 
-			q.queue('foo', 'init', function() {
+			q.queue('ent0', 'foo', 'init', function() {
 				ops.push('fooinit');
 			});
 
@@ -209,42 +244,5 @@ describe('RenderQueue', function() {
 				done();
 			}, 200);
 		});
-
-		it('paused+queued tasks from a previous namespace wait resume immediately', function(done) {
-			var ops = [];
-			var q = new RenderQueue({
-				foo: 100
-			});
-
-			q.pause();
-
-			q.queue('foo', 'destroy', function() {
-				ops.push('foodestroy');
-			});
-
-			q.queue('bar', 'destroy', function() {
-				ops.push('bardestroy');
-			});
-
-			expect(ops).toEqual([]);
-
-			q.queue('bar', 'init', function() {
-				ops.push('barinit');
-			});
-
-			q.queue('foo', 'init', function() {
-				ops.push('fooinit');
-			});
-
-			expect(ops).toEqual([]);
-
-			q.resume();
-			expect(ops).toEqual([ 'foodestroy', 'bardestroy', 'barinit' ]);
-
-			setTimeout(function() {
-				expect(ops).toEqual([ 'foodestroy', 'bardestroy', 'barinit', 'fooinit' ]);
-				done();
-			}, 200);
-		});
 	});
 });