I'm stumped
Basically, I have a http REST API to which i can make asynchronous requests to and provide a callback. I also have a web front end that will make calls to this api at arbitrary times in arbitrary locations asynchronously.
The problem is that the api requires a 'nonce'. a number that with each subsequent request is greater than the last. generating this number in such a manner is easy. ensuring that the request arrive in the right order.. not so much.
I need a way that out side my api module I can just make the function call and provide a callback. and the module will handle ensueing that any previously made calls will finish before it makes the new request.
There is of course the async module that allows one to run asynchronous function synchronously but it requires me to know all the actions I was to fire before had. with my current system a new request could potentially be added while its in the middle of processing the first one.
in general api calls are make like so
// the api module
var nextNonceFunc = function() {
var d = new Date();
var seconds = Math.floor(d.getTime() / 1000);
var nonce = seconds;
return function() {
return nonce++;
}
}();
var privateAPI = function(key, secret) {
var self = this;
var api = new API(key, secret, nextNonceFunc);
//information about the user
self.getInfo = function (callback) {
api.getInfo(callback);
};
}
// ... bla bla set up routes ect. -> enter the server request callback
// server module
// we make an api instance
var api = new privateAPI(key, secreet);
// some time later we make an api call
api.getInfo(function(err, data){
if (err) {
//handle error
}
// dosomthing with the data
})
I'm looking for a way to let the privateAPI object queue up the requests it gets.
After Kevin pointed out the existence of async.queue I was able to build the following wrapper
var async = require('async');
//actes as a wrapper for the passed api
module.exports = function(obj) {
var self = this;
var q = async.queue(function (task, callback) {
var args = task.args;
args.push(function(err, data){
task.cb(err, data);
if (err) return callback(err);
callback();
})
obj[task.func].apply(obj, args);
}, 1);
var keys = Object.keys(obj);
var buildFuncWrapper = function(i) {
self[keys[i]] = function() {
var args = Array.prototype.slice.call(arguments);
var task = {
func: keys[i],
args: args.slice(0, args.length - 1),
cb: args.slice(args.length - 1, args.length)[0]
};
q.push(task, function(){
if (global.DEBUG) console.log("[SYNC WRAPPER] finished wrapped call to: ", keys[i]);
})
}
}
var buildPropWrapper = function(i) {
Object.defineProperty(self, keys[i], {
get: function () { return obj[keys[i]]},
writable : false,
enumerable: true
})
}
for (var i = 0; i < keys.length; i++) {
if (typeof(obj[keys[i]]) === "function" ) {
buildFuncWrapper(i);
} else {
buildPropWrapper(i);
}
}
}
Basicly I can pass my existing API object to it and it will forward all the calls made to it's functions through the queue, as the queue has a concurrency of 1 only one call will will process at a time.
after that it's a fairly simple matter of ensuring that only one version of this wrapped api object exists.
Some sort of client side queue would work here. Note this is extremely quick and dirty and untested and assumes jquery for my convenience:
var ajaxQueue = function() {
var _queue = [];
var _isRunning = false;
function isRunning() {
return _isRunning;
}
function run(ajaxOptions) {
_isRunning = true;
$.ajax({
url : ajaxOptions.url,
method : ajaxOptions.method,
//etc
success : function() {
_isRunning = false;
if (ajaxOptions.callback) {
ajaxOptions.callback();
}
if (queue.length) {
var next = queue.shift();
run(next);
}
}
});
}
return {
add : function(ajaxOptions){
if (isRunning()) {
_queue.push(ajaxOptions);
// return the index of the current queue item
return _queue.length;
} else {
run(ajaxOptions);
}
},
remove : function(queueIndex) {
delete _queue[queueIndex];
}
}
}