Lets say you want to keep track of the last 20 touch events to happen on your page or app, or want to track updates to your database so if something goes wrong you have a record of the last operations that happened to help debug the problem.

If you are unfamiliar with a ring buffer - they are a constant sized array which will start to overwrite itself from the beginning once it has gone past its bounds. In general a ring buffer is useful to efficiently keep track of the last N instances of something happening.

How does it work

The easiest explanation is an example:

var buffer = new RingBuffer(3);
buffer.push('A');
buffer.push('B');
buffer.push('C');
console.log('Raw Buffer: ', buffer._data); // Prints "Raw Buffer: [A, B, C]"
buffer.push('D');
console.log('Raw Buffer: ', buffer._data); // Prints "Raw Buffer: [D, B, C]"

Each entry added to the buffer goes into the _data array until it reaches the length limit. At this point, further entries will start getting inserted at the beginning of the array overwriting the oldest entries.

Source Code

(function ()
{

function RingBuffer(len)
{
    // Track the current location in the data array that new entries will be inserted into
    this._pointer = 0;

    // Buffer used to store data
    this._data = [ ];

    // Maximum number of entries that will be stored in the ring buffer
    this._limit = len;
};

RingBuffer.prototype =
{
    _computeIndex: function (offset)
    {
        return (this._limit + this._pointer + offset) % this._limit;
    },
    push: function (entry)
    {
        // Add the entry to the buffer
        this._data[this._pointer] = entry;

        // Once the data has been added to the buffer, ensure that the next insertion index is
        // still valid. In order to ensure that we do not run off the end of the buffer, restrict
        // the pointer to the bounds of the buffer. Once the pointer goes past the limit, it will
        // reset itself to the beginning of the array and start overwriting the oldest entries.
        this._pointer = this._computeIndex(1);
    },
    get: function (index)
    {
        var bufferIndex = this._computeIndex(index);

        return data[bufferIndex];
    },
    reset: function ()
    {
        this._pointer = 0;

        this._data = [ ];
    },
    setLimit: function (newLimit)
    {
        this._limit = newLimit;
    },
    getList: function ()
    {
        // There are two parts of the buffer that need to be connected together to get the data in
        // the buffer ordered correctly. Since the pointer is the index of the last inserted entry,
        // the oldest entries are those after the pointer in the array (if any) and the newest are
        // those that are before it.
        var newest = this._data.slice(this._pointer);
        var oldest = this._data.slice(0, this._pointer);

        return newest.concat(oldest);
    },
    print: function ()
    {
        var arr = this.getList();

        for (var i = 0; i < arr.length; ++i)
        {
            console.log('---- Entry [' + i + '] ----');
            console.log(arr[i]);
        }
    }
};

return RingBuffer;

})();