Skip to content

Instantly share code, notes, and snippets.

@Blackskyliner
Last active October 5, 2016 09:21
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 Blackskyliner/8b1bafed326044fa4f8b1ba2627d1117 to your computer and use it in GitHub Desktop.
Save Blackskyliner/8b1bafed326044fa4f8b1ba2627d1117 to your computer and use it in GitHub Desktop.
Simple AngularJS $q Promise TaskQueue
const TaskQueue = [
'$q',
/**
* The TaskQueue will allow to limit parallel requests of Angular $q Promises (Tasks).
* While starting the creation and processing of the Promises it will adhere to the configured maxConcurrentTasks.
* If a Taks returns another Promise it will also be chained within this restricted environment.
*
* @param {$q} $q Anuglars $q Service to control Promise creation.
*
* @return {{
* maxConcurrentTasks: number,
* add: (function(Function.<*>, Function.<*>=, Function.<*>=, Function.<*>=)),
* process: (function(*=, *=, *=))
* }}
*
* @constructor
*/
function TaskQueue($q) {
const $$tasks = [];
let $$currentTaskCounter = 0;
const incTaskCounter = (passthroughValue) => {
$$currentTaskCounter += 1;
return passthroughValue;
};
const decTaskCounter = (passthroughValue) => {
$$currentTaskCounter -= 1;
return passthroughValue;
};
return {
/**
* Count of parallel Tasks, should be higher than 0, else it will not process Tasks.
*/
maxConcurrentTasks: 5,
/**
* Adds a function, which returns a value or promise to the Task stack.
*
* @param {function<*>} pFn function which returns a value or creates the promise to execute
* @param {function<*>} [successCallback] callback, if pFn resolved successfuly
* @param {function<*>} [errorCallback] callback, if pFn is rejected
* @param {function<*>} [notifyCallback] notification callback, if the pFn returned Promise provides this feature.
*/
add(pFn, successCallback, errorCallback, notifyCallback) {
$$tasks.push({
pFn,
successCallback,
errorCallback,
notifyCallback,
});
},
/**
* Start the processing of all Tasks and adhere to the maxConcurrentTasks.
*/
process() {
if ($$currentTaskCounter >= this.maxConcurrentTasks) {
return;
}
while ($$tasks.length && $$currentTaskCounter < this.maxConcurrentTasks) {
// Get and delete the task from the $$tasks array
const task = $$tasks.shift();
// Increment the task counter
$q.resolve(incTaskCounter())
// call the pFn and convert it to a promise if it's not already an promise.
// also register all of it's callbacks.
.then(() => $q.when(
task.pFn(),
task.successCallback,
task.errorCallback,
task.notifyCallback
))
// decrement the current task counter, so the "slot" will be available again
.then(decTaskCounter, decTaskCounter)
// chain process() so we add more possible Tasks
.then(() => this.process());
}
},
};
},
];
angular
.module('util')
.factory('TaskQueue', TaskQueue);
const controller = [
'TaskQueue', '$timeout',
function controller(TaskQueue, $timeout) {
for (let i = 0; i < 20; i++) {
TaskQueue.add(
() => $timeout(
() => i,
5000
),
(val) => {
console.log(`LOCAL: Task ${i} resolved`);
return val;
}
);
}
},
];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment