Have QueueRunner run specs iteratively if possible, fallback to recursion for async specs

This prevents the stack from growing as large for the normal cases and is giving a significant speedup for the performance suite
This commit is contained in:
Davis W. Frank and Sheel Choksi
2013-07-02 16:05:26 -07:00
parent 264b1fea50
commit 09fe7b0540
4 changed files with 44 additions and 31 deletions

View File

@@ -1299,19 +1299,26 @@ getJasmineRequireObj().QueueRunner = function() {
this.run(this.fns, 0); this.run(this.fns, 0);
}; };
QueueRunner.prototype.run = function(fns, index) { QueueRunner.prototype.run = function(fns, recursiveIndex) {
if (index >= fns.length) { var length = fns.length,
this.clearStack(this.onComplete); self = this,
return; iterativeIndex;
for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) {
var fn = fns[iterativeIndex];
if (fn.length > 0) {
attempt(function() { fn.call(self, function() {
self.clearStack(function() { self.run(fns, iterativeIndex + 1); });
});
});
return;
} else {
attempt(function() { fn.call(self); });
}
} }
var fn = fns[index]; if (iterativeIndex >= length) {
var self = this; this.onComplete();
if (fn.length > 0) {
attempt(function() { fn.call(self, function() { self.run(fns, index + 1); }); });
} else {
attempt(function() { fn.call(self); });
self.run(fns, index + 1);
} }
function attempt(fn) { function attempt(fn) {
@@ -1549,9 +1556,7 @@ getJasmineRequireObj().Suite = function() {
} }
function wrapChild(child) { function wrapChild(child) {
return function(done) { return function() { child.execute(); };
child.execute(done);
};
} }
}; };

View File

@@ -115,8 +115,8 @@ describe("QueueRunner", function() {
expect(completeCallback).toHaveBeenCalled(); expect(completeCallback).toHaveBeenCalled();
}); });
it("calls a provided garbage collection function with the complete callback when done", function() { it("calls a provided stack clearing function when done with async specs", function() {
var fn = jasmine.createSpy('fn'), var fn = function(done) { done() },
completeCallback = jasmine.createSpy('completeCallback'), completeCallback = jasmine.createSpy('completeCallback'),
clearStack = jasmine.createSpy('clearStack'), clearStack = jasmine.createSpy('clearStack'),
queueRunner = new j$.QueueRunner({ queueRunner = new j$.QueueRunner({
@@ -125,8 +125,11 @@ describe("QueueRunner", function() {
onComplete: completeCallback onComplete: completeCallback
}); });
clearStack.andCallFake(function(fn) { fn(); });
queueRunner.execute(); queueRunner.execute();
expect(clearStack).toHaveBeenCalledWith(completeCallback); expect(clearStack).toHaveBeenCalled();
expect(completeCallback).toHaveBeenCalled();
}); });
}); });

View File

@@ -12,19 +12,26 @@ getJasmineRequireObj().QueueRunner = function() {
this.run(this.fns, 0); this.run(this.fns, 0);
}; };
QueueRunner.prototype.run = function(fns, index) { QueueRunner.prototype.run = function(fns, recursiveIndex) {
if (index >= fns.length) { var length = fns.length,
this.clearStack(this.onComplete); self = this,
return; iterativeIndex;
for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) {
var fn = fns[iterativeIndex];
if (fn.length > 0) {
attempt(function() { fn.call(self, function() {
self.clearStack(function() { self.run(fns, iterativeIndex + 1); });
});
});
return;
} else {
attempt(function() { fn.call(self); });
}
} }
var fn = fns[index]; if (iterativeIndex >= length) {
var self = this; this.onComplete();
if (fn.length > 0) {
attempt(function() { fn.call(self, function() { self.run(fns, index + 1); }); });
} else {
attempt(function() { fn.call(self); });
self.run(fns, index + 1);
} }
function attempt(fn) { function attempt(fn) {

View File

@@ -93,9 +93,7 @@ getJasmineRequireObj().Suite = function() {
} }
function wrapChild(child) { function wrapChild(child) {
return function(done) { return function() { child.execute(); };
child.execute(done);
};
} }
}; };