chain.js - add function chaining capability
var Model = function() {};
Model.prototype.func1 = function() {
return this.c_delay().c_chain(function() {
console.log('func1 processing...');
this.c_next();
});
};
Model.prototype.func2 = function() {
return this.c_delay().c_chain(function() {
console.log('func2 processing...');
this.c_next();
});
};
chainify() the Model and call the functions:
chainify(Model);
var Obj = new Model();
Obj.func1().func2();
Console output:
func1 has 0 preceding functions
func2 has 1 preceding functions
func1 has 1 succeeding functions
func1 processing...
func2 has 0 succeeding functions
func2 processing...
Currently, you may use "this.manager.base" within the function handed over to c_chain() the access "Obj".
Same as above, but func1() looks like this:
Model.prototype.func1 = function() {
console.log('func1 has ' + this.c_getPredecessors().length + ' preceding functions');
return this.c_delay().c_chain(function() {
console.log('func1 has ' + this.c_getSuccessors().length + ' succeeding functions');
console.log('func1 processing...');
var self = this;
setTimeout(function() {
self.c_next();
}, 5000);
});
};
We get the same console output as above, but have to wait 5 seconds until the setTimeout() callback is fired.
func1 has 0 preceding functions
func2 has 1 preceding functions
func1 has 1 succeeding functions
func1 processing...
-> Wait 5 seconds
func2 has 0 succeeding functions
func2 processing...
This previous example demonstrates that chained functions are called in sequential order, as expected.
Same as above, but don't forget to pass in the "arguments" array whenever you want to access arguments within the function passed to c_chain() and/or want the callback capability, which is demonstrated here.
Model.prototype.func1 = function() {
console.log('func1 has ' + this.c_getPredecessors().length + ' preceding functions');
return this.c_delay().c_chain(function() {
console.log('func1 has ' + this.c_getSuccessors().length + ' succeeding functions');
console.log('func1 processing...');
var self = this;
setTimeout(function() {
self.c_next();
}, 5000);
}, arguments);
};
Now, we pass in a callback to func1(). It's called AFTER the internal asynchronous call to setTimeout() has fired and the function terminated.
chainify(Model);
var Obj = new Model();
Obj.func1(function() {
console.log('func1 processed!');
this.c_next();
}).func2();
Console output:
func1 has 0 preceding functions
func2 has 1 preceding functions
func1 has 1 succeeding functions
func1 processing...
-> Wait 5 seconds
func1 processed!
func2 has 0 succeeding functions
func2 processing...
For every function to be chained, a new slave object is created internally with the prototype of the master object (example above: "Model" = "master" object). The core code base of the current function is attached to the slave. Once the setup is done, a setTimeout() of 1 ms, pointing the core code base of the current function, is instantiated.
The object is then returned and eventually handed over to the next function. This function then decides whether to process all previously chained functions using c_process(), or delay their execution by clearing the timer using c_delay() and adding itself to the chain using c_chain(). This function may also clear all previously chained functions using c_clear().
Not surprisingly, the timer of the last function isn't cleared, because there simply isn't any subsequent function which could clear the timer. Thus, once the setTimeout fires after 1ms, processing of the chained functions begins.
Due to the asynchronous nature of the setTimeout() function, the execution of the function chain may be postponed. Take the model code of the first example shown above and the following:
chainify(Model);
var Obj = new Model();
Obj.func1().func2();
console.log('chainifying functions is cool!');
Console output:
chainifying functions is cool!
func1 has 0 preceding functions
func2 has 1 preceding functions
...
Depending on the use case, this may not be an issue at all. However, you may either prefer to use a callback on the last function or call exec() at the end.
chainify(Model);
var Obj = new Model();
Obj.func1().func2().exec();
console.log('chainifying functions is cool!');
Prints:
func1 has 0 preceding functions
func2 has 1 preceding functions
...
chainifying functions is cool!
I am currently investigating alternatives to achieve the latter without actually calling exec().
Happy function chaining :) Matthias Stumpp