Recently attended one of the front end of the interview, in which a handwritten together to realize parallel limitation Promise scheduler problem is the first time see, the time spent a lot of time, followed by second interview and written a similar restrictions (thinking) roughly parallel Promise subject, after specially, sums up the solution to this problem.

The title details

JS implements an asynchronous Scheduler with concurrency limits, ensuring that up to two tasks can be run simultaneously. Refine the Scheduler class for the following code so that the following programs can output normally:

class Scheduler {
  add(promiseCreator) { ... }
  // ...
}
   
const timeout = time => new Promise(resolve => {
  setTimeout(resolve, time);
})
  
const scheduler = new Scheduler();
  
const addTask = (time,order) => {
  scheduler.add(() => timeout(time).then(()=>console.log(order)))
}

addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');

// output: 2 3 1 4
Copy the code

The complete execution process:

  1. In fact, task 1 and task 2 are executed
  2. At 500ms, task 2 is complete, and task 3 starts to execute
  3. At 800ms, task 3 is complete, and task 4 starts to execute
  4. At 1000ms, task 1 is complete, and task 1 is output. Only four tasks are running
  5. At 1200ms, 4 is displayed

Their thinking

As can be seen, at most there are two parallel promises, and after the execution of one Promise is completed, a new Promise is executed, and the newly executed Promise does not affect the other Promise being executed.

In this case, the two apis promise.all () and promise.race () cannot be used. Promise.all() will wait for all promises to complete, and promise.race () will execute only one Promise.

In fact, the Promise is executed sequentially, and the first-in-first-out feature of the queue can be used. The add operation knowledge inserts Promise Creator into the queue each time to determine whether the current number of executions is less than 2. If the value is less than 2, then the Promise Creator execution will pop up from the queue and bind the then function to the Promise execution. When the then function is called, it indicates that the current Promise has been executed. Repeat the current operation, which can be seen as a recursive operation.

The actual use

With that in mind, you can start writing code to complement the class.

First, we define the array queue for storing Promise Creator, maxCount, runCOunts, and add to insert the Promise Generator into the queue.

class Scheduler {
  constructor() { this.queue = []; this.maxCount = 2; this.runCounts = 0; } add(promiseCreator) { this.queue.push(promiseCreator); }}Copy the code

Next comes the Request function: each time the Promise Generator is pulled from the queue and executed, the Promise should be recursively called to the Request function to execute the next Promise.

request() {
    if(! this.queue || ! this.queue.length || this.runCounts >= this.maxCount) {return;
    }
    this.runCounts++;

    this.queue.shift()().then(() => {
      this.runCounts--;
      this.request();
    });
}
Copy the code

Of course, we still need one last start function, and we need to launch two promises:

taskStart() {
    for (leti = 0; i < this.maxCount; i++) { this.request(); }}Copy the code

Complete code:

class Scheduler {
  constructor() {
    this.queue = [];
    this.maxCount = 2;
    this.runCounts = 0;
  }
  add(promiseCreator) {
    this.queue.push(promiseCreator);
  }
  taskStart() {
    for (leti = 0; i < this.maxCount; i++) { this.request(); }}request() {
    if(! this.queue || ! this.queue.length || this.runCounts >= this.maxCount) {return;
    }
    this.runCounts++;

    this.queue.shift()().then(() => {
      this.runCounts--;
      this.request();
    });
  }
}
   
const timeout = time => new Promise(resolve => {
  setTimeout(resolve, time);
})
  
const scheduler = new Scheduler();
  
const addTask = (time,order) => {
  scheduler.add(() => timeout(time).then(()=>console.log(order)))
}
  
  
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
  
scheduler.taskStart()
Copy the code

Output: