Since start, middle and finish are pretty much "verbs", why not make them actions of the object time? With that, an API like this would do:
$.time.start();
$.time.middle();
$.time.finish();
Further, a developer might run multiple instances of a timer to track more than just one something. Maybe a developer would like to run one timer for a page load while another set of timers for image loading. Thus, it would be better if we created instances:
//returns an instance id'ed by "pageload"
//passing blank creates only one anonymous timer
var pageLoadTimer = $.time('pageload');
//methods
pageLoadTimer.start();
pageLoadTimer.middle();
pageLoadTimer.finish();
//or simply chain
$.time('pageload').start();
//if you lose reference because you are not on the same scope
//or forgot to make one
//or didn't make one at all and relied on the globality of jQuery
//calling a timer of the same name returns an existing timer
$.time('pageload').middle();
Anyways, with that adapted code, a result would be (as verbose as I can):
$.time = (function () {
//our timer cache
var timers = {};
//extract common code
function getCurrentTimestamp() {
return new Date().getTime();
}
//constructor to our instances
//each with a name, time collection and difference collection
function Timer(timerName) {
this.name = timerName;
this.times = [];
this.perf = [];
this.lastPerfIndex = 0;
}
//since the initialization has been done in the constructor
//we remove it from start. but since start and middle does
//the same thing, we merge it as "ping"
Timer.prototype.ping = function () {
this.times.push(getCurrentTimestamp());
return this;
};
//calculate
//a developer might want to get performance data
//before actually finishing, thus the rename
Timer.prototype.calculate = function () {
//caching a few variables from the instance
var perf = this.perf,
times = this.times,
timesLength = times.length, //getting n-1 length
i = this.lastPerfIndex; //perfIndex explained further down
//ping our last time check
this.ping();
//now a reverse loop would only make the code longer
//a forward loop would make it much simpler
while (i <= timesLength) {
//now a developer might want to get performance data
//but you would not want to calculate the entire set again
//thus we store a last index, so the next call starts from here
this.lastPerfIndex = i;
//now since we are in order, use push instead
//of manually jotting down the index
perf.push(times[i + 1] - times[i]);
}
return perf;
};
//lets implement finish as a destroy function instead
Timer.prototype.finish = function(){
//remove this timer from cache
delete timers[this.name];
//return performance data
return this.calculate;
}
return function (timerName) {
if (timers[timerName]) {
//if timer with name exists, return existing
return timers[timerName];
} else {
//or cache and return a new one
//creating a new one does not start it immediately
//useful for stuff like preloading stuff or something
return timers[timerName] = new Timer(timerName);
}
};
}());
In the end, usage might look like this:
//create a timer
$.timer('foo');
//aw snap! i forgot to reference, we can retrieve it
var foo = $.timer('foo').ping(); //lets start!
//some lines later, let's mark this period
$.timer('foo').ping(); //by retrieval
foo.ping(); //by reference "foo"
//let's get the performance array without killing this timer
var perfAfterTenKlines = foo.calculaate();
//let's get the performance array gain after 20 lines
//this time it calculates only data since last calculate
var perfAfterTwentyKlines = foo.calculate();
//ok we're done. lets get the final data and call it a day
var perfAfterOneMlines = foo.finish();