编程题目: 方法链式调用时sleep

目录

1. 题目

实现一个类,满足一下功能:

  • const brush = new Brush("red")
  • brush.drawLine(10).sleep(1000).drawPoint([1, 3]).drawLine(100).sleep(2000).drawLine(10)
  • 期望输出
    • 输出: drawLine 10 with red
    • 1s 后输出: drawPoint [1, 3]
    • 输入:drawLine 100 with red
    • 再 2s 后输出:drawLine 10 with red

2. 思路

  • sleep 使得后面的链式调用都延时执行了,但其本质,返回的还是实例本身
  • 可以使用 Proxy 拦截该实例的所有函数执行, 然后使用 setTimeout 延时执行,但都返回当前实例, 那么就实现了延时性
  • 因为链式调用中,所有 sleep 都是同步执行的, 所以后面的 sleep 要把前面的时间累加上去
  • 为了性能考虑, 一个实例应该只创建一个 Proxy 所以用 WeakMap 缓存
const bucket = new WeakMap()

class Brush {
    constructor(color) {
        this.color = color
        this._totalTm = 0
    }
    drawLine(length) {
        console.log(new Date(), `drawLine ${length} with ${this.color}`)
        return this
    }
    drawPoint(coor) {
        console.log(new Date(), `drawPoint [${coor[0]}, ${coor[1]}]`)
        return this
    }
    sleep(tm) {
        const _this = this.__raw ?? this;
        let cached = bucket.get(_this)
        _this._totalTm += tm
        if (!cached) {
            cached = new Proxy(this, {
                get(target, key) {
                    if (key === '__raw') {
                        return _this
                    }
                    const rawValue = Reflect.get(...arguments)
                    if (typeof target[key] === 'function' && key !== 'sleep') {
                        // 除了 sleep 都拦截
                        const rawFn = rawValue
                        return new Proxy(rawFn, {
                            apply(...args) {
                                setTimeout(() => {
                                    Reflect.apply(...args)
                                }, _this._totalTm)
                                return cached
                            }
                        })
                    }
                    return rawValue
                }
            })
            bucket.set(this, cached)
        }
        return cached
    }
}

const brush = new Brush("red")
console.log(new Date(), 'run')
brush.drawLine(10)
    .sleep(1000).drawPoint([1, 3]).drawLine(100)
    .sleep(2000).drawLine(10)
    .sleep(1000).drawLine(8)
2023-03-08T08:00:00.468Z run
2023-03-08T08:00:00.470Z drawLine 10 with red
2023-03-08T08:00:01.472Z drawPoint [1, 3]
2023-03-08T08:00:01.473Z drawLine 100 with red
2023-03-08T08:00:03.471Z drawLine 10 with red
2023-03-08T08:00:04.471Z drawLine 8 with red

3. 再加一个 destroy , 可以清理掉所有延时执行的方法

思路:

  • 收集 timerId
  • 调用 destroy 清理它
const bucket = new WeakMap()

class Brush {
    constructor(color) {
        this.color = color
        this._totalTm = 0
        this._timerIds = []
    }
    drawLine(length) {
        console.log(new Date(), `drawLine ${length} with ${this.color}`)
        return this
    }
    drawPoint(coor) {
        console.log(new Date(), `drawPoint [${coor[0]}, ${coor[1]}]`)
        return this
    }
    destroy() {
        this._timerIds.forEach(clearTimeout)
    }
    sleep(tm) {
        const _this = this.__raw ?? this;
        let cached = bucket.get(_this)
        _this._totalTm += tm
        if (!cached) {
            cached = new Proxy(this, {
                get(target, key) {
                    if (key === '__raw') {
                        return _this
                    }
                    const rawValue = Reflect.get(...arguments)
                    if (typeof target[key] === 'function' && key !== 'sleep') {
                        // 除了 sleep 都拦截
                        const rawFn = rawValue
                        return new Proxy(rawFn, {
                            apply(...args) {
                                let id = setTimeout(() => {
                                    Reflect.apply(...args)
                                    _this._timerIds.splice(_this._timerIds.indexOf(id), 1)
                                }, _this._totalTm)
                                _this._timerIds.push(id)
                                return cached
                            }
                        })
                    }
                    return rawValue
                }
            })
            bucket.set(this, cached)
        }
        return cached
    }
}

const brush = new Brush("red")
console.log(new Date(), 'run')
brush.drawLine(10)
    .sleep(1000).drawPoint([1, 3]).drawLine(100)
    .sleep(2000).drawLine(10)
    .sleep(1000).drawLine(8)
setTimeout(() => {
    brush.destroy();
}, 3500)
2023-03-08T08:00:04.556Z run
2023-03-08T08:00:04.557Z drawLine 10 with red
2023-03-08T08:00:05.560Z drawPoint [1, 3]
2023-03-08T08:00:05.561Z drawLine 100 with red
2023-03-08T08:00:07.559Z drawLine 10 with red

4. 抽象化

  • 这个逻辑,其实与 Brush 类本身无关, 所以可以抽离成一个类,然后用 Brush 继承它
const bucket = new WeakMap()

const RAW = Symbol()

class Sleeper {
    constructor() {
        this._timerIds = []
        this._totalTm = 0
    }
    destroy() {
        this._timerIds.forEach(clearTimeout)
    }
    sleep(tm) {
        const _this = this[RAW] ?? this;
        let cached = bucket.get(_this)
        _this._totalTm += tm
        if (!cached) {
            cached = new Proxy(this, {
                get(target, key) {
                    if (key === RAW) {
                        return _this
                    }
                    const rawValue = Reflect.get(...arguments)
                    if (typeof target[key] === 'function' && key !== 'sleep') {
                        // 除了 sleep 都拦截
                        const rawFn = rawValue
                        return new Proxy(rawFn, {
                            apply(...args) {
                                let id = setTimeout(() => {
                                    Reflect.apply(...args)
                                    _this._timerIds.splice(_this._timerIds.indexOf(id), 1)
                                }, _this._totalTm)
                                _this._timerIds.push(id)
                                return cached
                            }
                        })
                    }
                    return rawValue
                }
            })
            bucket.set(this, cached)
        }
        return cached
    }
}

class Brush extends Sleeper {
    constructor(color) {
        super()
        this.color = color
    }
    drawLine(length) {
        console.log(new Date(), `drawLine ${length} with ${this.color}`)
        return this
    }
    drawPoint(coor) {
        console.log(new Date(), `drawPoint [${coor[0]}, ${coor[1]}]`)
        return this
    }
}

const brush = new Brush("red")
console.log(new Date(), 'run')
brush.drawLine(10)
    .sleep(1000).drawPoint([1, 3]).drawLine(100)
    .sleep(2000).drawLine(10)
    .sleep(1000).drawLine(8)
setTimeout(() => {
    brush.destroy();
}, 3500)

2023-03-08T08:00:08.147Z run
2023-03-08T08:00:08.148Z drawLine 10 with red
2023-03-08T08:00:09.151Z drawPoint [1, 3]
2023-03-08T08:00:09.152Z drawLine 100 with red
2023-03-08T08:00:11.150Z drawLine 10 with red

日期: 2023-03-02

Validate