实现一个 LazyMan 函数

参考:https://blog.csdn.net/qq_39261142/article/details/110425286

1. 描述

  • 可以链式调用
  • sleep() 函数会阻塞后面的链式调用
const lazyMan = new LazyMan('jack')
lazyMan.eat('apple').sleep(5000).eat('hamburger').sleep(3000).eat('pear')
/*
最终结果:
    My named jack
    I am eatting apple
    I am sleeping...
    after 5000 ms
    I am eatting hamburger
    I am sleeping...
    after 3000 ms
    I am eatting pear
*/

2. 实现

2.1 sleep函数

正常的 sleep 函数,如果是为了链式调用 Promise 可以这样写

function sleep(time) {
    return new Promise((resolve)=> {
        setTimeout(() => {
            resolve()
        }, time)
    })
}

但是这里如果为了实现阻塞链式调用而使用 Promise 的方式或者直接延时调用来实现,实际上是一个误区,会给代码增加很多难度,而且还不一定可以实现其功能

class LazyMan {
    constructor(name) {
        this.name = name
        this.task = []      // 任务队列
        console.log(`My named ${name}`)
    }
    sleep(time) {
        console.log(`I am sleeping...`)
        setTimeout(() => {
            console.log(`after ${time} ms`)
            return this
        },time)
    }
    eat(food) {
        console.log(`I am eatting ${food}`)
        return this
    }
}
const lazyMan = new LazyMan('jack')
lazyMan.sleep(5000).eat('apple')    //  property 'eat' of undefined

上面代码中,链式调用并不会主动去阻塞,延时 return this 并不会阻塞链式调用往下进行,因此调用到 eat('apple') 时,由于没有及时的返回 this,自然没有 eat 方法,所以就报错了

这个时候想到 Promise 的实现是用一个任务队列来进行阻塞的,可以也使用这样的方式,与 Promise 不同的是,实现这个并没有那么多的判断条件,只需要将每个任务执行完然后继续执行下一个任务就行

class LazyMan {
    constructor(name) {
        this.name = name
        this.task = []      // 任务队列
        console.log(`My named ${name}`)

        // 这里使用异步调用next()是为了确保所有链式调用都被添加到task[]才开始执行任务
        setTimeout(() => {
            this.next()
        })
    }

    sleep(time) {
        this.task.push(() => {
            console.log(`I am sleeping...`)
            setTimeout(() => {
                console.log(`after ${time} ms`)
                this.next()
            },time)

        })
        return this
    }

    eat(food) {
        this.task.push(() => {
            console.log(`I am eating ${food}`)
            this.next()
        })
        return this
    }

    next() {
        let fn = this.task.shift()
        fn && fn()  // if(fn) fn()
    }
}

const lazyMan = new LazyMan('jack')
lazyMan.eat('apple').sleep(5000).eat('hamburger').sleep(3000).eat('pear')