实现 LazyMan 任务队列

使用任务队列实现链式调用和延迟执行

问题

实现一个 LazyMan,支持以下调用方式:

LazyMan('Jack')
  .sleep(1)
  .eat('lunch')
  .sleepFirst(2)
  .eat('dinner')

// 输出:
// (等待 2 秒)
// Wake up after 2 seconds
// Hi, I'm Jack
// (等待 1 秒)
// Wake up after 1 seconds
// Eat lunch
// Eat dinner

要求:

  • sleep(seconds) - 延迟执行后续任务
  • sleepFirst(seconds) - 延迟执行,但优先级最高
  • eat(food) - 输出吃什么
  • 支持链式调用

解答

class LazyManClass {
  constructor(name) {
    this.queue = []
    
    // 初始任务:打招呼
    this.queue.push(() => {
      console.log(`Hi, I'm ${name}`)
      this.next()
    })
    
    // 异步启动队列,确保链式调用的任务都已入队
    setTimeout(() => {
      this.next()
    }, 0)
  }
  
  // 执行下一个任务
  next() {
    const task = this.queue.shift()
    task && task()
  }
  
  sleep(seconds) {
    this.queue.push(() => {
      setTimeout(() => {
        console.log(`Wake up after ${seconds} seconds`)
        this.next()
      }, seconds * 1000)
    })
    return this // 链式调用
  }
  
  sleepFirst(seconds) {
    // 插入队列头部,优先执行
    this.queue.unshift(() => {
      setTimeout(() => {
        console.log(`Wake up after ${seconds} seconds`)
        this.next()
      }, seconds * 1000)
    })
    return this
  }
  
  eat(food) {
    this.queue.push(() => {
      console.log(`Eat ${food}`)
      this.next()
    })
    return this
  }
}

function LazyMan(name) {
  return new LazyManClass(name)
}

测试

LazyMan('Jack')
  .sleep(1)
  .eat('lunch')
  .sleepFirst(2)
  .eat('dinner')

执行流程分析

// 1. 构造函数执行,queue = [sayHi]
// 2. .sleep(1) 执行,queue = [sayHi, sleep1]
// 3. .eat('lunch') 执行,queue = [sayHi, sleep1, eatLunch]
// 4. .sleepFirst(2) 执行,queue = [sleep2, sayHi, sleep1, eatLunch]
// 5. .eat('dinner') 执行,queue = [sleep2, sayHi, sleep1, eatLunch, eatDinner]
// 6. setTimeout 回调执行,开始消费队列

关键点

  • 任务队列:用数组存储所有待执行的任务函数
  • 异步启动setTimeout(0) 确保所有链式调用完成后再执行队列
  • next 机制:每个任务执行完必须调用 next() 触发下一个任务
  • sleepFirst 实现:使用 unshift 将任务插入队列头部
  • 链式调用:每个方法返回 this