I met an interview question last year:

Design an asynchronous task scheduler that can have at most two asynchronous tasks running at the same time. The completed asynchronous tasks are queued, and the asynchronous tasks to be queued are queued in the sequence in which they are added.

class Scheduler {
  constructor() {... } add(promiseCreator) {... }}/ / use cases:
const timeout = time= >
  new Promise(resolve= > {
    setTimeout(resolve, time)
  })

const scheduler = new Scheduler()

const addTask = (time, order) = > {
  scheduler.add((a)= > timeout(time)).then((a)= > console.log(time, order))
}

addTask(1000.'1')
addTask(500.'2')
addTask(300.'3')
addTask(400.'4')
/ / output:
/ / 500 '2'
/ / 300 '3'
/ / 1000 '1'
/ / 400 '4'
Copy the code

I wrote an async await version of this:

Async await version:

class Scheduler {
  constructor() {
    this.waitQueue = []
    this.count = 0
  }
  async add(promiseCreator) {
    this.count >= 2 && await new Promise(resolve= > this.waitQueue.push(resolve))
    this.count++
    const res = await promiseCreator()
    this.count--
    this.waitQueue.length && this.waitQueue.shift()()
    return res
  }
}

const timeout = time= >
  new Promise(resolve= > {
    setTimeout(resolve, time)
  })

const scheduler = new Scheduler()

const addTask = (time, order) = > {
  scheduler.add((a)= > timeout(time)).then((a)= > console.log(time, order))
}

addTask(1000.'1')
addTask(500.'2')
addTask(300.'3')
addTask(400.'4')
// Execution result:
/ / 500 '2'
/ / 300 '3'
/ / 1000 '1'
/ / 400 '4'
Copy the code

Execution process:

Async is a generator with its own executor, i.e. the former is the syntactic sugar of the latter, and the core is the same. They allow a function to execute in segments, yielding execution when the function encounters await and yield. So the execution flow of the above program is as follows:

  • Synchronization task:
  1. addTask(1000, '1')Perform toconst res = await promiseCreator()Hand over the executive power and carry out the next step
  2. addTask(500, '2')With 1
  3. addTask(300, '3')At this timecount = 2So the implementation toawait new Promise(resolve => this.waitQueue.push(resolve))“To hand over executive power and take the next step
  4. addTask(400, '4')With 3
  • Asynchronous tasks:
  1. time=500Timer Resolve,addTask(500, '2')The function regains execution and starts executing the following code:
this.count-- //count = 1
this.waitQueue.length && this.waitQueue.shift()() // Restore execution of the first asynchronous task in waitQueue, resolve, addTask(300, '3'
return res Then (() => console.log(time, order))
Copy the code
  1. addTask(300, '3')Function regains execution authority and executes toconst res = await promiseCreator()Surrender of execution
  2. time=300Resolve timer, the follow-up process is the same as 5
  3. addTask(400, '4')The function regains execution, as in 6
  4. time=1000Resolve timer, the follow-up process is the same as 5
  5. time=400Resolve timer, the follow-up process is the same as 5

Promise version:

Later when I reviewed the Promise, I tried to write a version with the Promise

class Scheduler {
  constructor() {
    this.promiseCreatorQueue = []
    this.waitQueue = []
    this.count = 0
  }
  add(promiseCreator) {
    if (this.count >= 2) return new Promise(resolve= > {
      this.waitQueue.push(resolve)
      this.promiseCreatorQueue.push(promiseCreator)
    }).then((a)= > {
      this.count++
      return this.promiseCreatorQueue.shift()().then((a)= > {
        this.count--
        this.waitQueue.length && this.waitQueue.shift()()
      })
    })
    this.count++
    return promiseCreator().then((a)= > {
      this.count--
      this.waitQueue.length && this.waitQueue.shift()()
    })
  }
}

const timeout = time= >
  new Promise(resolve= > {
    setTimeout(resolve, time)
  })

const scheduler = new Scheduler()

const addTask = (time, order) = > {
  scheduler.add((a)= > timeout(time)).then((a)= > console.log(time, order))
}

addTask(1000.'1')
addTask(500.'2')
addTask(300.'3')
addTask(400.'4')
// Execution result:
/ / 500 '2'
/ / 300 '3'
/ / 1000 '1'
/ / 400 '4'
Copy the code

Execute the process

PromiseCreator () returns a Promise Promise that will start the next promiseCreator to start a new asynchronous task

conclusion

As seen from the code, async await greatly simplifies the code and logic.