Skip to content
This repository was archived by the owner on Aug 7, 2024. It is now read-only.
This repository was archived by the owner on Aug 7, 2024. It is now read-only.

LazyMan 有几样写法,你知道么? #36

Open
@fi3ework

Description

@fi3ework

题目

实现一个 LazyMan,可以按照以下方式调用:

  • LazyMan("Hank")

    输出:

    Hi! This is Hank!

  • LazyMan("Hank").sleep(10).eat("dinner")

    输出:

    Hi! This is Hank!

    等待10秒..

    Wake up after 10

    Eat dinner

  • LazyMan("Hank").eat("dinner").eat("supper")

    输出:

    Hi This is Hank!

    Eat dinner

    Eat supper

  • LazyMan("Hank").sleepFirst(5).eat("supper")

    等待5秒

    Wake up after 5

    Hi This is Hank!

    Eat supper

实现

1. callback

纯 callback 实现, 每个注册的事件的最后会调用对象队列中的下一个事件。

class LazyMan {
  constructor(name) {
    this.name = name
    this.sayName = this.sayName.bind(this)
    this.next = this.next.bind(this)
    this.queue = [this.sayName]
    setTimeout(this.next, 0)
  }

  callByOrder(queue) {
    let sequence = Promise.resolve()
    this.queue.forEach(item => {
      sequence = sequence.then(item)
    })
  }
  
  next(){
  	const currTask = this.queue.shift()
    currTask && currTask()
  }

  sayName() {
    console.log(`Hi! this is ${this.name}!`)
    this.next()
  }

  holdOn(time) {
    setTimeout(() => {
      console.log(`Wake up after ${time} second`)
      this.next()
    }, time * 1000)
  }

  sleep(time) {
    this.queue.push(this.holdOn(time))
    return this
  }

  eat(meal) {
    this.queue.push(() => {
      console.log(`eat ${meal}`)
      this.next()
    })
    return this
  }

  sleepFirst(time) {
    this.queue.unshift(this.holdOn(time))
    return this
  }
}

2. Promise

手工在每次方法执行后通过 then 调整 Promise 链的序列,缺点是因为 sleepFirst 要强行插入 Promise 链的第一位,要单独抽象出一部分逻辑来前置它的 Promise。

class LazyMan {
  constructor(name) {
    this.name = name
    this._preSleepTime = 0
    this.sayName = this.sayName.bind(this)
    this.p = Promise.resolve().then(() => {
      if (this._preSleepTime > 0) {
        return this.holdOn(this._preSleepTime)
      }
    }).then(this.sayName)
  }

  sayName() {
    console.log(`Hi! this is ${this.name}!`)
  }

  holdOn(time) {
    return new Promise(resolve => {
      setTimeout(() => {
        console.log(`Wake up after ${time} second`)
        resolve()
      }, time * 1000)
    })
  }

  sleep(time) {
    this.p = this.p.then(
      () => this.holdOn(time)
    )
    return this
  }

  eat(meal) {
    this.p = this.p.then(() => {
      console.log(`eat ${meal}`)
    })
    return this
  }

  sleepFirst(time) {
    this._preSleepTime = time
    return this
  }
}

3. Promise + 队列

在对象内部维护一个队列,让所有的事件都变成异步的,然后在内部通过 Promise.resolve.then() 来将队列的执行启动推迟到下一个 eventloop,这样做逻辑更清楚,所有事件都由队列来管理。

class LazyMan {
  constructor(name) {
    this.name = name
    this.sayName = this.sayName.bind(this)
    this.queue = [this.sayName]
    Promise.resolve().then(() => this.callByOrder(this.queue))
  }

  callByOrder(queue) {
    let sequence = Promise.resolve()
    this.queue.forEach(item => {
      sequence = sequence.then(item)
    })
  }

  sayName() {
    return new Promise((resolve) => {
      console.log(`Hi! this is ${this.name}!`)
      resolve()
    })
  }

  holdOn(time) {
    return () => new Promise(resolve => {
      setTimeout(() => {
        console.log(`Wake up after ${time} second`)
        resolve()
      }, time * 1000)
    })
  }

  sleep(time) {
    this.queue.push(this.holdOn(time))
    return this
  }

  eat(meal) {
    this.queue.push(() => {
      console.log(`eat ${meal}`)
    })
    return this
  }

  sleepFirst(time) {
    this.queue.unshift(this.holdOn(time))
    return this
  }
}

4. Promise + async

基本思路与第 2 种方法相同,不同的地方只在于使用了 async 来顺序执行队列。

class LazyMan {
  constructor(name) {
    this.name = name
    this.sayName = this.sayName.bind(this)
    this.queue = [this.sayName]
    setTimeout(async () => {
      for (let todo of this.queue) {
        await todo()
      }
    }, 0)
  }

  callByOrder(queue) {
    let sequence = Promise.resolve()
    this.queue.forEach(item => {
      sequence = sequence.then(item)
    })
  }

  sayName() {
    return new Promise((resolve) => {
      console.log(`Hi! this is ${this.name}!`)
      resolve()
    })
  }

  holdOn(time) {
    return () => new Promise(resolve => {
      setTimeout(() => {
        console.log(`Wake up after ${time} second`)
        resolve()
      }, time * 1000)
    })
  }

  sleep(time) {
    this.queue.push(this.holdOn(time))
    return this
  }

  eat(meal) {
    this.queue.push(() => {
      console.log(`eat ${meal}`)
    })
    return this
  }

  sleepFirst(time) {
    this.queue.unshift(this.holdOn(time))
    return this
  }
}

TODO

  • yield 的实现版本

总结

整个过程其实还挺有意思的,多写几种方法挺练 Promise 和整个异步调用的思维的..

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions