Skip to content

Instantly share code, notes, and snippets.

@tmarsteel
Last active July 1, 2017 11:32
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 tmarsteel/56ab3b17a0148c1fd33431a2a57295d7 to your computer and use it in GitHub Desktop.
Save tmarsteel/56ab3b17a0148c1fd33431a2a57295d7 to your computer and use it in GitHub Desktop.
A Promises/A+ based transactional Queue
var TransactionQueue = function() {
var items = [],
currentTransactionPromise = null,
queuedDuringTransaction = [],
_self = this;
this.pushItem = function(item) {
if (currentTransactionPromise != null)
{
console.log("Adding item WITHIN transaction: %o", item);
queuedDuringTransaction.push(item);
}
else
{
console.log("Adding item OUTSIDE OF transaction: %o", item);
items.push(item);
}
};
this.consume = function(nItems, callback) {
if (currentTransactionPromise != null)
{
console.log("--> Consume invoked during running consume -> delaying...");
return ~function(nItems, callback) {
return currentTransactionPromise.then(function() {
return _self.consume(nItems, callback);
});
}(nItems, callback);
}
else
{
~function(transactionItems, callback) {
console.log("--> Consuming %o", transactionItems);
currentTransactionPromise = new Promise(function(resolve, reject) {
if (transactionItems.length == 0)
resolve();
callback(transactionItems, resolve, reject);
});
currentTransactionPromise.then(function() {
if (queuedDuringTransaction.length != 0)
{
items = items.concat(queuedDuringTransaction);
queuedDuringTransaction = [];
}
currentTransactionPromise = null;
}, function() {
// put things back into order
items = items.concat(transactionItems).concat(queuedDuringTransaction);
queuedDuringTransaction = [];
currentTransactionPromise = null;
});
}(
items.splice(0, Math.min(items.length, nItems)),
callback
);
return currentTransactionPromise;
}
};
this.hasItems = function() {
if (currentTransactionPromise != null)
{
return true;
}
else
{
return items.length > 0;
}
};
};
@tmarsteel
Copy link
Author

Usage example and unit tests:

Array.prototype.equals = function (array) {
    // if the other array is a falsy value, return
    if (!array)
        return false;

    // compare lengths - can save a lot of time 
    if (this.length != array.length)
        return false;

    for (var i = 0, l=this.length; i < l; i++) {
        // Check if we have nested arrays
        if (this[i] instanceof Array && array[i] instanceof Array) {
            // recurse into the nested arrays
            if (!this[i].equals(array[i]))
                return false;       
        }           
        else if (this[i] != array[i]) { 
            // Warning - two different object instances will never be equal: {x:20} != {x:20}
            return false;   
        }           
    }       
    return true;
};
// Hide method from for-in loops
Object.defineProperty(Array.prototype, "equals", {enumerable: false});

// basic consume functionality
var queue = new TransactionQueue();
queue.pushItem(1);
queue.pushItem(2);
queue.pushItem(3);
queue.pushItem(4);
queue.pushItem(5);
queue.consume(2, function(items, resolve, reject) {
    if (!(items.equals([1, 2])))
    {
        console.log("Error: Basic queueing 1: %o", items);
    }
    else
    {
        console.log("Basic queueing 1: successful");
    }
    resolve();
});
queue.consume(3, function(items, resolve, reject) {
    if (!(items.equals([3, 4, 5])))
    {
        console.log("Error: Basic queueing 2: %o", items);
    }
    else
    {
        console.log("Basic queueing 2: successful");
    }
    resolve();
});

// failed transaction
queue = new TransactionQueue();
queue.pushItem(1);
queue.pushItem(2);
queue.consume(1, function(items, resolve, reject) {
    reject();
});
queue.consume(2, function(items, resolve, reject) {
    if (!(items.equals([1, 2])))
    {
        console.log("Error: Failed transaction not handled correctly: %o", items);
    }
    else
    {
        console.log("Failed transaction: handled correctly");
    }
    resolve();
});

// queue during successful transaction
queue = new TransactionQueue();
queue.pushItem(1);
queue.pushItem(2);
queue.consume(1, function(items, resolve, reject) {
    window.setTimeout(function() {
        resolve();
    }, 500);
});
queue.pushItem(3);
queue.consume(2, function(items, resolve, reject) {
    if (!(items.equals([2, 3])))
    {
        console.log("Error: Items queued during successful transaction not re-joined properly");
    }
    else
    {
        console.log("Items queued during successful transaction: handled correctly");
    }
    resolve();
});

// queue during failing transaction
queue = new TransactionQueue();
queue.pushItem(1);
queue.pushItem(2);
queue.consume(1, function(items, resolve, reject) {
    window.setTimeout(function() {
        reject();
    }, 500);
});
queue.pushItem(3);
queue.consume(3, function(items, resolve, reject) {
    if (!(items.equals([1, 2, 3])))
    {
        console.log("Error: Items queued during failing transaction not re-joined properly");
    }
    else
    {
        console.log("Items queued during failing transaction: handled correctly");
    }
    resolve();
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment