Skip to content

Instantly share code, notes, and snippets.

@duncanbeevers
Created March 17, 2009 23:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save duncanbeevers/80841 to your computer and use it in GitHub Desktop.
Save duncanbeevers/80841 to your computer and use it in GitHub Desktop.
/*
Buffer up calls to groups of functions, unwind stack when precondition is met.
Calls to a buffered function prior to its preconditions being met are queued
and are later played back to the function when the precondition is met.
Groups of functions can share a common call stack by sharing a precondition object,
which is a simple function reference.
For simple control over how calls are played back, provide a tag with each buffered
function declaration. When tags are used, buffered calls are first sorted by their
tag, then by their timestamp, giving you in-order, per-tag function playback.
var precondition = function(){ return obj.isBarrierCleared(); },
obj = {
_barrierCleared: false,
_stack: [],
isBarrierCleared: function() {
return this._barrierCleared;
},
fn1: function(i) {
this._stack.push('fn1:' + i);
}.bufferingFunction(precondition, 'tagged A'),
fn2: function(i) {
this._stack.push('fn2:' + i);
}.bufferingFunction(precondition, 'tagged B'),
clearBarrier: function() {
this._barrierCleared = true;
}
};
obj.fn1(1);
obj.fn2(1);
obj.fn1(2);
obj.clearBarrier();
obj.fn1(3);
obj._stack;
>> ["fn1:1", "fn1:2", "fn1:3", "fn2:1"]
If you need very fine control over the order in which calls are played back to the
original functions, you may provide a sorting function which will sort buffered calls
prior to playback. A sorting function should accept two call stack objects, A and B,
compare them, and return -1, 0, 1 depending on whether A should occur before B, A and B
are equivalent, or B should occur before A.
*/
Function.prototype.bufferingFunction = function(precondition, unspoolSorter) {
var call_buffer = Function.preconditionBufferingGroup(precondition, unspoolSorter),
originalFunction = this,
outerFunction = function() {
call_buffer.push(originalFunction, arguments, this, outerFunction, unspoolSorter);
if (precondition(this)) { call_buffer.unwind(); }
};
return outerFunction;
};
Function.preconditionBufferingGroup = function(precondition, unspoolSorter) {
if (!this._buffering_function_groups) { this._buffering_function_groups = []; }
if (undefined === precondition._buffering_group_id) {
precondition._buffering_group_id = this._buffering_function_groups.length;
}
var buffering_group_id = precondition._buffering_group_id;
if (!this._buffering_function_groups[buffering_group_id]) {
this._buffering_function_groups[buffering_group_id] = function() {
var stack = [];
return {
push: function(fn, args, bind_to, outerFunction, unspoolSorter) {
stack.push({
fn: fn,
outerFunction: outerFunction,
args: args,
bind_to: bind_to,
timestamp: new Date(),
tag: unspoolSorter
});
},
unwind: function() {
if (!stack.length) { return; }
var c,
tagAndTimestampSorter = function(a, b) {
var cmp = function(a, b) {
if (a.localeCompare && b.localeCompare) {
return a.localeCompare(b);
}
if (a > b) { return -1; }
if (a < b) { return 1; }
return 0;
};
return (cmp(a.tag, b.tag) || cmp(a.timestamp, b.timestamp));
};
if (unspoolSorter instanceof Function) {
stack.sort(unspoolSorter);
} else if (unspoolSorter) {
stack.sort(tagAndTimestampSorter);
};
while (c = stack.shift()) {
var fn = c.fn,
args = c.args,
bind_to = c.bind_to;
fn.apply(bind_to, args);
}
}
};
}();
};
return this._buffering_function_groups[buffering_group_id];
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment