Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript Scheduler implementation using promises

I was solving this interesting javascript problem (interview question) and I got stuck on how I can implement this using promises.

Problem:

Write a scheduler in JavaScript that accepts max number of concurrent tasks as a parameter and schedules tasks (each task may take arbitrary time to complete).

Please note that we need to only execute "n" (concurrent) tasks at a time before proceeding to execute other tasks.

This is my implementation:

var exampleTaskA = function () {
    setTimeout(function () {
        console.log('Task A Done');
    }, 1000);
};

function TaskRunner(concurrency) {
    this.limit = concurrency;
    this.store = [];
    this.len = this.store.length;
}

TaskRunner.prototype.push = function (task) {
    this.store.push(task);
    function getWorker(store, limit) {
        if(!store.length) return;

        if(store.length <= limit) {
            const cur = store.shift();
            if(cur) cur();
            getWorker(store, limit);
        }
    }

    getWorker(this.store, this.limit);
}

var task = new TaskRunner(2);
console.log(task.push(exampleTaskA));
console.log(task.push(exampleTaskA));
console.log(task.push(exampleTaskA)); 
console.log(task.push(exampleTaskA));
console.log(task.push(exampleTaskA));
console.log(task.push(exampleTaskA));
console.log(task.push(exampleTaskA));

How can I use promises / async await to implement this? Should I wrap everything around a promise before pushing?

Can someone enlighten?

like image 887
TechnoCorner Avatar asked Apr 20 '26 03:04

TechnoCorner


1 Answers

So if you can return a promise from your task, you can tie into the the promise's then() to alert you when the task is finished and when you can start another.

Here's an example similar to yours with a couple changes: we don't care about the length of the queue — you only want to know how many active jobs are present. So you can increment active when starting jobs a decrement it when jobs are done.

There are a lot of ways, I'm sure to do this, but here's the outline of one idea:

const exampleTaskA = (name) => new Promise(resolve => setTimeout(function() {
  console.log(`Task ${name} Done`);
  resolve()
}, Math.floor(Math.random() * 2000)))

function TaskRunner(concurrency) {
  this.limit = concurrency;
  this.store = [];
  this.active = 0;
}

TaskRunner.prototype.next = function() {
  if (this.store.length) this.runTask(...this.store.shift())
}

TaskRunner.prototype.runTask = function(task, name) {
  this.active++
  console.log(`Scheduling task ${name} current active: ${this.active}`)
  task(name).then(() => {
    this.active--
    console.log(`Task ${name} returned, current active: ${this.active}`)
    this.next()
  })
}
TaskRunner.prototype.push = function(task, name) {
  if (this.active < this.limit) this.runTask(task, name)
  else {
    console.log(`queuing task ${name}`)
    this.store.push([task, name])
  }
}

var task = new TaskRunner(2);
task.push(exampleTaskA, 1)
task.push(exampleTaskA, 2)
task.push(exampleTaskA, 3)
task.push(exampleTaskA, 4)
task.push(exampleTaskA, 5)
task.push(exampleTaskA, 6)
task.push(exampleTaskA, 7)
like image 195
Mark Avatar answered Apr 21 '26 16:04

Mark