EDITED CODE
const enqueued_ids = [];
const enqueued_tasks = [];
let running = 0;
let concurrency = 10;
/*
Set how many tasks will run concurrently.
*/
function set_concurrency (max) {
concurrency = max;
};
/*
Check whether a task with the specified id is already in the queue.
*/
function in_queue (id) {
if ([undefined, null].includes(id)) { return false; }
return enqueued_ids.includes(id);
};
/*
Add the id for a task to the list of enqueued ids.
*/
function enqueue_id (id) {
if ([undefined, null].includes(id)) { return; }
enqueued_ids.push(id);
};
/*
Remove the id after the task has finished.
*/
function dequeue_id (id) {
if ([undefined, null].includes(id)) { return; }
enqueued_ids.splice(enqueued_ids.indexOf(id), 1);
};
/*
Start a new task from the queue.
*/
function fire_task () {
/*
If there are pending tasks in the queue ...
*/
if (enqueued_tasks.length > 0) {
/*
... and there are less tasks running than can run concurrently ...
*/
if (running < concurrency) {
/*
... get a task from the queue ...
*/
const enqueued_task = enqueued_tasks.shift();
const id = enqueued_task['id'];
const task = enqueued_task['task'];
/*
... and start it.
*/
const fired_task = new Promise(function (resolve, reject) {
running++;
task()
.then(() => { resolve(); })
.catch(() => { reject(); });
});
/*
The task has finished.
*/
fired_task.then(() => {
running--;
dequeue_id(id);
fire_task();
}).catch(() => {
running--;
dequeue_id(id);
fire_task();
});
}
}
};
/*
Add tasks to the queue and have them execute without caring about the result.
You should set the number of concurrent tasks with the 'set_concurreny (max)' function, if
you wish for more than one task to be executed at a time.
If a specific task should be unique and only have one instance of itself running, give it
an id and check whether it is already in the queue with the 'in_queue (id)' function.
*/
async function fire_and_forget (id, task) {
/*
If an id is defined, check whether it is already in the queue.
You should check this yourself before executing this function to avoid an exception.
*/
if (in_queue(id)) { throw 'Task with id \'' + id + '\' is already in queue.'; }
/*
If the id is valid, add it to the list of enqueued ids.
*/
enqueue_id(id);
/*
Bind the parameters to the task.
*/
const bound_task = task.bind.apply(task, [null].concat([].slice.call(arguments).slice(2)));
/*
Add the task to the queue.
*/
enqueued_tasks.push({
'id': id,
'task': bound_task
});
/*
Start a new task from the queue.
*/
fire_task();
};
module.exports.set_concurrency = set_concurrency;
module.exports.in_queue = in_queue;
module.exports.fire_and_forget = fire_and_forget;
/*
TEST SCENARIO --- Remove code beneath for production
*/
// Dummy task
async function long_write_task(subject, body) {
await new Promise((resolve) => setTimeout(resolve, 1000));
console.log([subject, body]);
};
// Set concurrency to 3 tasks.
set_concurrency(3);
// Start 10 tasks.
for(let i = 0; i < 10; i++){
// Check if id already queued.
// Can't be if we just assign the loop iteration number as id, but is best practice.
// If you don't care about running an identical task twice, leave id 'undefined'.
if (!in_queue(i)) {
fire_and_forget(i, long_write_task, 'subject ' + i, 'body ' + i);
}
}