Skip to content

Instantly share code, notes, and snippets.

@Ariex
Last active September 11, 2018 01:06
Show Gist options
  • Save Ariex/55463970d2bdebf6fba867d62259c7e5 to your computer and use it in GitHub Desktop.
Save Ariex/55463970d2bdebf6fba867d62259c7e5 to your computer and use it in GitHub Desktop.
a simple task scheduler
(async function() {
console.clear();
class Task {
constructor(func, id) {
this._func = func;
// GUID generate method comes from: https://stackoverflow.com/a/2117523/903505
this.id = id !== void(0) ? id : ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c=>(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16));
this.promise = null;
}
async run() {
if (this.promise === null) {
this.promise = new Promise((resolve,reject)=>{
if (this._func.constructor.name === "AsyncFunction") {
this._func().then(data=>{
resolve(data);
}).catch(err=>reject(err));
} else {
try {
resolve(this._func());
} catch (e) {
reject(e);
}
}
});
}
return this.promise;
}
}
class TaskScheduler {
constructor(maxConcurrent=4) {
this.MaxConcurrent = maxConcurrent;
this.IdleCount = maxConcurrent;
this.TaskQueue = [];
this.PendingQueue = [];
this._workingProcess = null;
this._forceToStop = false;
}
addTasks(...tasks) {
this.TaskQueue.push(...tasks);
}
async start() {
if (this._workingProcess === null) {
this._workingProcess = new Promise((resolve,reject)=>{
let start = ()=>{
if (this._forceToStop || this.TaskQueue.length + this.PendingQueue.length === 0) {
Promise.all(this.PendingQueue.map(t=>t.run())).then(()=>resolve());
} else {
if (this.PendingQueue.length < this.MaxConcurrent) {
this.PendingQueue.push(...this.TaskQueue.splice(0, this.MaxConcurrent-this.PendingQueue.length));
this.PendingQueue.map(t=>t.run().then(d=>{
this.PendingQueue = this.PendingQueue.filter(e=>e.id !== t.id);
start();
return d;
}));
}
}
}
start();
}).then(()=>{
this._forceToStop = false;
this._workingProcess = null;
});
}
return this._workingProcess;
}
stop() {
this._forceToStop = true;
}
async run(...tasks) {
this.addTasks(...tasks);
return this.start();
}
}
let ts = new TaskScheduler(3);
let timeConsumingTask = i=>async()=>{
return new Promise((resolve,reject)=>{
let timeout = Math.random() * (Math.random() > 0.5 ? 3000 : 500);
console.log(`Task ${i} start running [${timeout}]`);
setTimeout(function() {
console.log(`Task ${i} completed`);
resolve(timeout);
}, timeout);
});
}
let syncTask = i=>()=>console.log(`Sync task ${i}`);
let taskQueueTask = ts.run(...new Array(10).fill(10).map((e, i)=>{return i>5?new Task(syncTask(i), i):new Task(timeConsumingTask(i), i);})).then(()=>{
console.log("tasks done")
if(ts.TaskQueue.length > 0){
console.log("resume remainning task in 2s");
setTimeout(()=>ts.start().then(()=>console.log("all tasks have completed")), 2000);
}
});
setTimeout(()=>{
ts.addTasks(...new Array(10).fill(10).map((e, i)=>new Task(timeConsumingTask(i+10), i+10)));
}, 1000);
setTimeout(()=>{
ts.stop();
}, 2500);
await taskQueueTask;
}
)();
@Ariex
Copy link
Author

Ariex commented Sep 11, 2018

fix the stop function;

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