admin管理员组

文章数量:1414940

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 plete).

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));

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 plete).

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?

Share Improve this question edited Aug 14, 2018 at 22:50 TechnoCorner asked Aug 14, 2018 at 21:51 TechnoCornerTechnoCorner 5,17510 gold badges45 silver badges89 bronze badges 11
  • the problem appears odd, since almost nothing in JS actually runs "concurrently" unless it's an inherently async operation (e.g. AJAX) – Alnitak Commented Aug 14, 2018 at 21:55
  • This was asked to me in one of the interview questions in top tech panies so I'm trying to solve it using Promises @Alnitak – TechnoCorner Commented Aug 14, 2018 at 21:55
  • I believe in this case, the concurrency means that the user has to execute only "X" items from the queue at a time. – TechnoCorner Commented Aug 14, 2018 at 21:57
  • Unless it's a queue of async operations, you can't. If you ever pass a task that's actually synchronous it'll run to the exclusion of all others. – Alnitak Commented Aug 14, 2018 at 21:59
  • They are .. please take a look at the task.. it's a setTimeout task() .. Correct me if i'm wrong. – TechnoCorner Commented Aug 14, 2018 at 22:01
 |  Show 6 more ments

3 Answers 3

Reset to default 6

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)

class TaskScheduler {
    constructor(concurrency) {
        this.limit = concurrency;
        this.active = 0;
        this.pool = [];
    }
    push(task) {
        this.pool.push(task);
        if (this.active < this.limit) {
            this.active += 1;
            this.execute(this.pool.shift());
        }
    }
    execute(task) {
        task().then(val => {
            console.log(`${val} executed`);
            this.active -= 1
            if (this.pool.length && this.active < this.limit) {
                this.execute(this.pool.shift());
            }
        });
    }
}

const task = () => new Promise((res, rej) => setTimeout(() => res('task'), 4000));
const task2 = () => new Promise((res, rej) => setTimeout(() => res('task'), 200));

const scheduler = new TaskScheduler(2);
scheduler.push(task);
scheduler.push(task2);
scheduler.push(task2);
scheduler.push(task2);
scheduler.push(task);
scheduler.push(task);
scheduler.push(task);
scheduler.push(task);

window.addEventListener("load", function() { // this is without promises

function Task(name) {
    this.isDone = false;
    this.name = name;
}

Task.prototype.run = function() {
    setTimeout(()=>{
        this.isDone = true;
    }, 3000);
}

function TaskScheduler(limit) {
    this.limit = limit;
    this.active = 0;
    this.pendingTasks = [];
    this.runningTasks = [];
}

TaskScheduler.prototype.runTask = function(task) {
    this.active++;
    task.run();
}

TaskScheduler.prototype.init = function() {
    var interval = setInterval(() => {
        // check and clean running tasks
        this.runningTasks = this.runningTasks.filter((task) => {
            if (task.isDone) {
                this.active--;
            }
            return !task.isDone;
        });

        while(this.pendingTasks.length) {
            if (this.active < this.limit) {
                var task = this.pendingTasks.pop();
                this.runTask(task);
                this.runningTasks.push(task);
            } else {
                break;
            }
        }

        if (!this.pendingTasks.length) {
            clearInterval(interval);
        }
    }, 0);
}

TaskScheduler.prototype.push = function(task) {
    this.pendingTasks.push(task);
}

var taskSecheduler = new TaskScheduler(2);
taskSecheduler.push(new Task(1));
taskSecheduler.push(new Task(2));
taskSecheduler.push(new Task(3));
taskSecheduler.push(new Task(4));
taskSecheduler.push(new Task(5));
taskSecheduler.push(new Task(6));
taskSecheduler.init();

});

本文标签: asynchronousJavaScript Scheduler implementation using promisesStack Overflow