dojox
Version:
Dojo eXtensions, a rollup of many useful sub-projects and varying states of maturity – from very stable and robust, to alpha and experimental. See individual projects contain README files for details.
140 lines (138 loc) • 5 kB
JavaScript
define(['dojo/_base/lang', 'dojo/Deferred', 'dojo/when'], function(lang, Deferred, when){
// summary:
// This is a store wrapper that provides prioritized operations. One can include a "priority" property in the options
// property that is a number representing the priority, for get, put, add, remove, or query calls. The associated
// action will execute before any other actions in the queue that have a lower priority. The priority queue will also
// throttle the execution of actions to limit the number of concurrent actions to the priority number. If you submit
// an action with a priority of 3, it will not be executed until there are at most 2 other concurrent actions.
var queue = []; // the main queue
var running = 0; // how many actions are running
function processQueue(){
// here we pull actions of the queue and run them
// each priority level has it's own sub-queue, we go
// from highest to lowest, stopping if we hit the minimum
// priority for the currently running operations
for(var priority = queue.length - 1; priority >= running; priority--){
// get the sub-queue for this priority
var queueForPriorityLevel = queue[priority];
var action = queueForPriorityLevel && queueForPriorityLevel[queueForPriorityLevel.length - 1];
if(action){
queueForPriorityLevel.pop();
// track the number currently running
running++;
try{
action.executor(function(){
running--;
// once finished, process the next one in the queue
processQueue();
});
}catch(e){
action.def.reject(e);
processQueue();
}
}
}
}
function deferredResults(){
// because a query result set is not merely a promise that can be piped through, we have
// to recreate the full result set API to defer the execution of query, within each of the
// methods we wait for the promise for the query result to finish. We have to wrap this
// in an object wrapper so that the returned promise doesn't further defer access to the
// query result set
var deferred = new Deferred();
return {
promise: {
total: {
// allow for lazy access to the total
then: function(callback, errback){
return deferred.then(function(wrapper){
return wrapper.results.total;
}).then(callback, errback);
}
},
forEach: function(callback, thisObj){
// wait for the action to be executed
return deferred.then(function(wrapper){
// now run the forEach
return wrapper.results.forEach(callback, thisObj);
});
},
map: function(callback, thisObj){
return deferred.then(function(wrapper){
return wrapper.results.map(callback, thisObj);
});
},
filter: function(callback, thisObj){
return deferred.then(function(wrapper){
return wrapper.results.filter(callback, thisObj);
});
},
then: function(callback, errback){
return deferred.then(function(wrapper){
return when(wrapper.results, callback, errback);
});
}
},
resolve: deferred.resolve,
reject: deferred.reject
};
}
return function(store, config){
config = config || {};
var priorityStore = lang.delegate(store);
// setup the handling for each of these methods
['add', 'put', 'query', 'remove', 'get'].forEach(function(method){
var original = store[method];
if(original){
priorityStore[method] = function(first, options){
options = options || {};
var def, executor;
if(options.immediate){
// if immediate is set, skip the queue
return original.call(store, first, options);
}
// just in case a method calls another method with the same args,
// default to doing the second call immediately.
options.immediate = true;
if(method === 'query'){
// use the special query result deferral
executor = function(callback){
// execute and resolve the wrapper
var queryResults = original.call(store, first, options);
def.resolve({results: queryResults});
// wait until the query results are done before performing the next action
when(queryResults, callback, callback);
};
// the special query deferred
def = deferredResults();
}else{
executor = function(callback){
// execute the main action (for get, put, add, remove)
when(original.call(store, first, options), function(value){
def.resolve(value);
callback(); // done
},
function(error){
def.reject(error);
callback();
});
};
// can use a standard deferred
def = new Deferred();
}
// add to the queue
var priority = options.priority > -1 ?
options.priority :
config.priority > -1 ?
config.priority : 4;
(queue[priority] || (queue[priority] = [])).push(
{executor: executor, def: def});
// get the next one off the queue
processQueue();
return def.promise;
};
}
});
return priorityStore;
};
});