懒汉式——LazyMan(任务队列应用)

发布于:2025-09-10 ⋅ 阅读:(21) ⋅ 点赞:(0)

代码

/**
 * 懒汉式 LazyMan
 * @description
 * 注意:
 * 1. 在每次执行的任务里面调用next(任务按顺序执行),而不是添加任务后执行next(立即执行task,多个任务会同时触发)
 * 2. 使用setTimeout确保构造函数执行完再启动任务队列
 */
class LazyMan {
    constructor(name) {
        this.name = name
        // 任务队列
        this.tasks = []
        const task = () => {
            console.log(`Hi I am ${name}`);
            this.next();
        }
        this.tasks.push(task)

        // 确保构造函数执行完再启动任务队列
        setTimeout(() => {
            this.next()
        }, 0)
    }

    // 启动一次任务队列(执行第一个任务)
    next() {
        const task = this.tasks.shift()
        if (task) task()
    }

    eat(food) {
        const task = () => {
            console.log(`Eat ${food}~`);
            this.next();
        }
        this.tasks.push(task)
        return this
    }

    sleep(seconds) {
        const task = () => {
            setTimeout(() => {
                console.log(`Wake up after ${seconds}s`);
                this.next();
            }, seconds * 1000)
        }
        this.tasks.push(task)
        return this
    }

    sleepFirst(seconds) {
        const task = () => {
            setTimeout(() => {
                console.log(`Wake up after ${seconds}s`);
                this.next();
            }, seconds * 1000)
        }
        this.tasks.unshift(task)
        return this
    }
}

// new LazyMan("Hank").eat("dinner");
// Hi I am Hank
// Eat dinner~

// new LazyMan("Hank").sleep(2).eat("dinner");
// Hi I am Hank
// (2秒后) Wake up after 2s
// Eat dinner~

// new LazyMan("Hank").sleepFirst(2).eat("dinner");
// (2秒后) Wake up after 2s
// Hi I am Hank
// Eat dinner~

在每次执行的任务里面调用next,而不是添加任务后执行next

每次执行的任务里面调用 next,确保任务按顺序执行。

如果添加任务后就立刻执行next,会导致每一个加入的任务都立刻执行,违背了串行,类似于并行执行。

使用setTimeout确保构造函数执行完再启动任务队列

先看构造函数里如果 立刻调用 this.next() 会怎样

class LazyMan {
  constructor(name) {
    this.tasks = []
    const task = () => {
      console.log(`Hi I am ${name}`)
      this.next()
    }
    this.tasks.push(task)

    // ⚠️ 这里如果立刻调用 next
    this.next()
  }

  next() {
    const task = this.tasks.shift()
    if (task) task()
  }

  eat(food) {
    const task = () => {
      console.log(`Eat ${food}`)
      this.next()
    }
    this.tasks.push(task)
    return this
  }
}

new LazyMan("Tom").eat("apple")

执行顺序

  1. new LazyMan("Tom") 执行构造函数。

    • tasks.push("Hi I am Tom")
    • 立刻 next() → 执行 “Hi I am Tom”
  2. 构造函数还没结束,已经执行掉第一个任务了。

  3. 接着调用 .eat("apple"),才把 Eat apple 放到队列里。

    • 但是此时队列已经空了(第一个任务执行完时队列已经没东西了)。
    • 所以 Eat apple 永远不会执行。

👉 结果:

Hi I am Tom

而不是期望的:

Hi I am Tom
Eat apple

这就是「来不及入队」的意思。


再看 setTimeout(..., 0) 的情况

class LazyMan {
  constructor(name) {
    this.tasks = []
    const task = () => {
      console.log(`Hi I am ${name}`)
      this.next()
    }
    this.tasks.push(task)

    // ✅ 延迟到构造函数执行完后才启动队列
    setTimeout(() => {
      this.next()
    }, 0)
  }

  next() {
    const task = this.tasks.shift()
    if (task) task()
  }

  eat(food) {
    const task = () => {
      console.log(`Eat ${food}`)
      this.next()
    }
    this.tasks.push(task)
    return this
  }
}

new LazyMan("Tom").eat("apple")

执行顺序

  1. new LazyMan("Tom") 执行构造函数。

    • tasks.push("Hi I am Tom")
    • setTimeout(..., 0) 安排一个异步任务(等同步代码执行完再跑)。
  2. 构造函数执行完毕,继续执行 .eat("apple")

    • tasks.push("Eat apple")
  3. 同步代码跑完,进入事件循环 → 执行 setTimeout 回调里的 this.next()

    • 这时队列是:[Hi I am Tom, Eat apple]
    • 所以会先执行 "Hi I am Tom",再执行 "Eat apple"

👉 结果:

Hi I am Tom
Eat apple

完全符合预期。


网站公告

今日签到

点亮在社区的每一天
去签到