【面試系列】LazyMan的ES6實現

最近某次筆試看到了一個比較有意思的LazyMan問題,基於本身的一些基礎作了一些解答,回來結合了一些相關資料,本身從新代碼實現了一遍。javascript

問題描述

實現一個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
 
以此類推。

思路分析

看到這個題目,首先注意到一些關鍵點聯想到對應的方案點。java

  1. LazyMan(「Hank」)調用,而不是new LazyMan(「Hank」)建立 => 工廠方法返回new對象
  2. 鏈式調用實現 => 每次調用返回this
  3. sleep須要等待10s => setTimeout實現sleep
  4. setTimeout會放到事件列表中排隊,繼續執行後面的代碼,可是題目中sleep須要阻塞後續操做。 => 考慮將sleep封裝成promise,使用async/await等待sleep,實現阻塞。
  5. sleepFirst每次在最開始執行,考慮將sleepFirst插入到事件第一個執行。

所以,首先咱們須要taskQueue記錄事件列表,直到調用完成後再執行taskQueue裏面的事件。怎麼實現調用完成後纔開始執行taskQueue的事件呢?
答案:setTimeout機制。setTimeout(function(){xxx},0)不是立馬執行,這是由於js是單線程的,有一個事件隊列機制,setTimeoutsetInterval的回調會插入到延遲時間塞入事件隊列中,排隊執行。promise

源碼展現

class _LazyMan {
    constructor(name) {
        this.taskQueue = [];
        this.name = name;
        this.timer = null;
        this.sayHi();
    }
    // 每次調用時清楚timer,上一次設置的執行taskQueue就不會運行。
    // 從新設置timer,會在下一次調用完後進入執行。
    // 當全部調用結束後,就會順利執行taskQueue隊列裏的事件
    next() {
        clearTimeout(this.timer);
        this.timer = setTimeout(async () => {
            // 執行taskQueue隊列裏的事件
            for (let i = 0; i < this.taskQueue.length; i++) {
                await this.taskQueue[i]();
            }
        });
        return this;
    }
    sayHi() {
        this.taskQueue.push(() => {
            console.log('Hi! This is ' + this.name);
        });
        return this.next();
    }
    eat(str) {
        this.taskQueue.push(() => {
            console.log('Eat ' + str);
        });
        return this.next();
    }
    beforSleep(time) {
        // unshift插入到事件的第一個
        this.taskQueue.unshift(() => this.sleepPromise(time));
        return this.next();
    }
    sleep(time) {
        this.taskQueue.push(() => this.sleepPromise(time));
        return this.next();
    }
    // sleep的Promise對象,用於給async/await來阻塞後續代碼執行
    sleepPromise(time) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('wake up after ' + time);
                resolve();
            }, time * 1000);
        });
    }
}

function LazyMan(name) {
    return new _LazyMan(name);
}

調用測試:
LazyMan('Herry').beforSleep(1).eat('dinner').sleep(2).eat('check');
輸出:
image.pngasync

相關文章
相關標籤/搜索