BOOMMMMMM async error queues now done, this is so sexy

This commit is contained in:
Peter Cottle 2012-09-15 17:05:13 -07:00
parent dbd7913803
commit 459d104fdb
5 changed files with 81 additions and 175 deletions

View file

@ -1,175 +1,72 @@
/** var Animation = Backbone.Model.extend({
* Util classes defaults: {
*/ duration: 300,
closure: null
},
function CommandQueue() { validateAtInit: function() {
this.commands = []; if (!this.get('closure')) {
this.consumeTimeout = null; throw new Error('give me a closure!');
this.initialDelay = 400;
}
CommandQueue.prototype.add = function(command) {
this.commands.push(command);
this.touchTimer();
};
CommandQueue.prototype.touchTimer = function() {
if (this.consumeTimeout) {
return;
} }
this.consumeTimeout = setTimeout(_.bind(function() { },
initialize: function(options) {
this.validateAtInit();
},
run: function() {
this.get('closure')();
}
});
var AnimationQueue = Backbone.Model.extend({
defaults: {
animations: [],
index: 0,
callback: null
},
initialize: function(options) {
if (!options.callback) {
console.warn('no callback');
}
},
add: function(animation) {
if (!animation instanceof Animation) {
throw new Error("Need animation not something else");
}
this.get('animations').push(animation);
},
start: function() {
this.set('index', 0);
this.next(); this.next();
}, this), this.initialDelay); },
};
CommandQueue.prototype.reset = function() { finish: function() {
this.consumeTimeout = null; this.get('callback')();
}; },
CommandQueue.prototype.next = function() { next: function() {
if (this.commands.length == 0) { // ok so call the first animation, and then set a timeout to call the next
this.reset(); // TODO: animations with callbacks!!
return; var animations = this.get('animations');
} var index = this.get('index');
if (index >= animations.length) {
// execute the top command by passing it into the engine
var toExecute = this.commands.shift(0);
var callback = _.bind(function() {
this.next();
}, this);
gitEngine.execute(toExecute, callback);
};
/******************
* Planning:
here is the major flow:
someone types in a command ->
make a new command object. if error, give immediate feedback, dont append to queue
if not error ->
append command object to queue
Command Queue ->
consume commands at a certain rate (either instantly if just added, or with an interval
Execute command -> (usually a git engine thing)
Wait for git engine command to finish
when done, execute next command (if more)
so two levels of Async-ness:
command queue slowly consumes commands
GitEngine executes commands, which will have async bits to them (such as popping off commits for a
rebase)
*/
function Scheduler(closures, options) {
if (!closures || !closures.length) {
throw new Error('invalid params');
}
this.closures = closures;
this.options = options || {};
this.interval = this.options.interval || 400;
this.done = false;
this.timeOut = null;
this.index = 0;
}
Scheduler.prototype.start = function() {
// set the first interval
this.index = 0;
this.done = false;
this.setNext();
};
Scheduler.prototype.setNext = function(interval) {
this.timeOut = setTimeout(_.bind(function() {
this.step();
}, this),
interval || this.interval);
};
Scheduler.prototype.finish = function() {
this.done = true;
clearTimeout(this.timeOut);
this.timeOut = null;
if (this.options.callback) {
this.options.callback();
}
};
Scheduler.prototype.step = function() {
if (this.done) {
return;
}
var results = this.closures[this.index]() || {};
this.index++;
if (results.done || this.index >= this.closures.length) {
this.finish(); this.finish();
return; return;
} }
this.setNext(results.interval);
};
var next = animations[index];
var duration = next.get('duration');
next.run();
/** this.set('index', index + 1);
* class Breather
*/
function Breather(closure, baseline, delta, period, wait) {
this.delta = delta;
this.baseline = baseline;
this.closure = closure;
this.t = 0;
this.interval = 1/40 * 1000; // 40fps
var period_in_seconds = period || time.breathePeriod;
this.period = 2 * Math.PI * 1000 * period_in_seconds;
this.interpolationFunction = TWEEN.Easing.Cubic.EaseInOut;
if (wait) {
setTimeout(_.bind(function() { setTimeout(_.bind(function() {
this.start();
}, this), wait);
} else {
this.start();
}
}
Breather.prototype.start = function() {
this.t = 0;
this.next(); this.next();
}; }, this), duration);
},
Breather.prototype.next = function() { });
this.timeout = setTimeout(
_.bind(function() {
this.breathe();
}, this),
this.interval);
};
Breather.prototype.stop = function() {
clearTimeout(this.timeout);
};
Breather.prototype.breathe = function() {
this.t += this.interval;
var value = Math.sin(this.t / this.period) * this.delta + this.baseline;
this.closure(value);
this.next();
};

View file

@ -3,7 +3,7 @@ var CommitCollection = Backbone.Collection.extend({
}); });
var CommandCollection = Backbone.Collection.extend({ var CommandCollection = Backbone.Collection.extend({
model: Command model: Command,
}); });
var CommandBuffer = Backbone.Model.extend({ var CommandBuffer = Backbone.Model.extend({
@ -36,10 +36,6 @@ var CommandBuffer = Backbone.Model.extend({
// timeout existence implies its being processed // timeout existence implies its being processed
return; return;
} }
// process first element now
this.popAndProcess();
// always set the timeout, regardless of buffer size
this.setTimeout(); this.setTimeout();
}, },
@ -51,7 +47,7 @@ var CommandBuffer = Backbone.Model.extend({
}, },
popAndProcess: function() { popAndProcess: function() {
var popped = this.buffer.pop(); var popped = this.buffer.shift(0);
var callback = _.bind(function() { var callback = _.bind(function() {
this.setTimeout(); this.setTimeout();
}, this); }, this);

View file

@ -33,6 +33,7 @@ var Command = Backbone.Model.extend({
err instanceof CommandResult || err instanceof CommandResult ||
err instanceof GitError) { err instanceof GitError) {
this.set('error', err); this.set('error', err);
this.set('status', 'error');
} else { } else {
throw err; throw err;
} }

View file

@ -682,16 +682,24 @@ GitEngine.prototype.dispatch = function(command, callback) {
this.commandOptions = command.get('supportedMap'); this.commandOptions = command.get('supportedMap');
this.generalArgs = command.get('generalArgs'); this.generalArgs = command.get('generalArgs');
command.set('status', 'processing'); // set up the animation queue
this[command.get('method') + 'Starter']();
// TODO: move into animation thing
var whenDone = _.bind(function() { var whenDone = _.bind(function() {
command.set('status', 'finished'); command.set('status', 'finished');
callback(); callback();
}, this); }, this);
whenDone(); this.animationQueue = new AnimationQueue({
callback: whenDone
});
command.set('status', 'processing');
this[command.get('method') + 'Starter']();
// TODO (get rid of)
for (var i = 0; i < 10; i++) {
this.animationQueue.add(new Animation({closure: function() { console.log(Math.random()); }}));
}
this.animationQueue.start();
}; };
GitEngine.prototype.addStarter = function() { GitEngine.prototype.addStarter = function() {

View file

@ -45,6 +45,10 @@ p.commandLine, p.commandLineResult {
background-color: black; background-color: black;
} }
p.commandLine.error {
background-color: red;
}
p.commandLine.inqueue { p.commandLine.inqueue {
background-color: yellow; background-color: yellow;
color: black; color: black;