LazyMan問題

分享一道面試題:
實現一個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 suppernode

以此類推。面試

涉及到的技術點:
鏈式編程
JS流程控制
JS事件循環機制Promise
。。。編程

方法1:
問題的關鍵是如何實現任務的順序執行。
參考node.js,在Express有一個相似的東西叫中間件,這個中間件和咱們這裏的吃飯、睡覺等任務很相似。
每個中間件執行完成後會調用next()函數,這個函數用來調用下一個中間件。
對於這個問題,咱們也能夠利用類似的思路來解決。
首先建立一個任務隊列,而後利用next()函數來控制任務的順序執行:
[JavaScript] 純文本查看 複製代碼
?promise

function LazyMan(name) {app

// 建立一個任務隊列
let taskList = [];
// 建立lazyman對象
let _lazyMan = {
    // 執行下一步 的方法
    next() {
        // 抽取任務隊列中的第一個任務
        let task = taskList.shift();
        // 若是存在該任務,就調用該任務
        task && task()
    },
    // sayHi(打招呼) 方法
    sayHi(name) {
        // 在任務隊列 最後面 追加任務
        taskList.push(() => {
            // 打招呼
            console.log(`Hi! This is ${name}!`);
            // 該任務完成以後,調用下一步的方法
            this.next()
        })
        // return this 爲了實現鏈式編程
        return this
    },
    // sleep(睡覺) 方法
    sleep(time) {
        // 在任務隊列 最後面 追加任務
        taskList.push(() => {
            // 開啓定時器
            setTimeout(() => {
                // 輸入 多少秒以後醒來
                console.log(`Wake up after ${time}`);
                // 該任務完成以後,調用下一步的方法
                this.next()
            }, time * 1000);
        })
        // return this 爲了實現鏈式編程
        return this
    },
    eat(food) {
        taskList.push(() => {
            console.log(`Eat ${food}~`);
            this.next()
        })
        return this
    },
    // sleepFirst(先睡) 方法
    sleepFirst(time) {
        // 在任務隊列 最前面 添加任務
        taskList.unshift(() => {
            // 開啓定時器
            setTimeout(() => {
                console.log(`Wake up after ${time}`);
                // 該任務完成以後,調用下一步的方法
                this.next()
            }, time * 1000);
        })
        // return this 爲了實現鏈式編程
        return this
    }
}

// 手動調用 sayHi方法
_lazyMan.sayHi(name)
// 使用定時器,讓任務隊列在同步線程完成以後再執行
setTimeout(() => {
    _lazyMan.next()
}, 0);
// 暴露 lazyman對象
return _lazyMan

}函數

// LazyMan("Hank")
// LazyMan("Hank").sleep(5).eat("dinner")
// LazyMan("Hank").eat("dinner").eat("supper")
LazyMan("Hank").sleepFirst(5).eat("supper")this

方法2:prototype

  1. 看題目輸出示例,能夠肯定這是擬人化的輸出,也就是說:應該編寫一個類來定義一類人,叫作LazyMan。能夠輸出名字、吃飯、睡覺等行爲。

 2. 從輸出的句子能夠看出,sleepFrist的優先級是最高的,其餘行爲的優先級一致。
 3. 從三個例子來看,都得先調用LazyMan來初始化一我的,才能繼續後續行爲,因此LazyMan是一個接口。
 4. 句子是按調用方法的次序進行順序執行的,是一個隊列。線程

[JavaScript] 純文本查看 複製代碼
?code

// 採用模塊模式來編寫代碼
(function (window, undefined) {

// 建立一個任務隊列
var taskList = [];
// {
//     'msg': 'LazyMan', // 消息名
//     'args': 'Hank'    // 參數列表
// }
// 訂閱
function subscribe() {
    var args = Array.prototype.slice.call(arguments);

    if (args.length < 1) {
        throw new Error("subscribe 參數不能爲空!");
    }
    // 建立任務
    var task = {
        msg: args[0], // 消息名
        args: args.slice(1) // 參數列表
    }

    // 除非是 sleepFirst 向前添加,不然向後追加
    if (task.msg == "sleepFirst") {
        taskList.unshift(task);
    } else {
        taskList.push(task);
    }
}

// 發佈
function publish() {
    if (taskList.length > 0) {
        // 調用 run(執行)方法
        run(taskList.shift());
    }
}

// 類
function LazyMan() {};

LazyMan.prototype.eat = function (str) {
    subscribe("eat", str);
    return this;
};

LazyMan.prototype.sleep = function (num) {
    subscribe("sleep", num);
    return this;
};

LazyMan.prototype.sleepFirst = function (num) {
    subscribe("sleepFirst", num);
    return this;
};

// 輸出文字
function lazyManLog(str) {
    console.log(str);
}

// 具體方法
// 打招呼
function lazyMan(str) {
    lazyManLog("Hi!This is " + str + "!");
    publish();
}
// 吃
function eat(str) {
    lazyManLog("Eat " + str + "~");
    publish();
}
// 睡
function sleep(num) {
    setTimeout(function () {
        lazyManLog("Wake up after " + num);

        publish();
    }, num * 1000);

}
// 先睡
function sleepFirst(num) {
    setTimeout(function () {
        lazyManLog("Wake up after " + num);

        publish();
    }, num * 1000);
}

// run(執行)方法:
function run(option) {
    var msg = option.msg,
        args = option.args;

    switch (msg) {
        case "lazyMan":
            lazyMan.apply(null, args);
            break;
        case "eat":
            eat.apply(null, args);
            break;
        case "sleep":
            sleep.apply(null, args);
            break;
        case "sleepFirst":
            sleepFirst.apply(null, args);
            break;
        default:
            ;
    }
}

// 暴露接口
window.LazyMan = function (str) {
    subscribe("lazyMan", str);

    setTimeout(function () {
        publish();
    }, 0);

    return new LazyMan();
};

})(window);

// LazyMan("Hank")
// LazyMan("Hank").sleep(5).eat("dinner")
// LazyMan("Hank").eat("dinner").eat("supper")
LazyMan("Hank").sleepFirst(5).eat("supper")

Promise版:
[JavaScript] 純文本查看 複製代碼
?

function _LazyMan(name) {

this.promiseGetters = [];

var makePromise = function  () {
    var promiseObj = new Promise(function(resolve, reject){
        console.log("Hi! This is " + name + "!");

        resolve();
    })

    return promiseObj;
}

this.promiseGetters.push(makePromise);

// 在各個Promise的then函數中,將任務序列穿起來
var self = this;
var sequence = Promise.resolve();
// Promise.resolve 等價於
// var sequence = new Promise(function (resolve, reject) {
//     resolve();
// })
setTimeout(function(){
    for (var i = 0; i < self.promiseGetters.length; i++) {
        var nowPromiseGetter = self.promiseGetters[i];
        var thenFunc = (function (nowPromiseGetter) {
            return function  () {
                return nowPromiseGetter()
            }
        })(nowPromiseGetter);

        sequence = sequence.then(thenFunc);
    };

}, 0); // 在下一個事件循環啓動任務

}

_LazyMan.prototype.eat = function(name) {

var makePromise = function  () {
    var promiseObj = new Promise(function(resolve, reject){
        console.log("Eat " + name + "~");

        resolve();
    })

    return promiseObj;
}

this.promiseGetters.push(makePromise);

return this; // 實現鏈式調用

}

_LazyMan.prototype.sleep = function(time) {

var makePromise = function  () {
    var promiseObj = new Promise(function(resolve, reject){

        setTimeout(function(){

            console.log("Wake up after " + time + "s!");

            resolve();

        }, time * 1000);
    })

    return promiseObj;
}

this.promiseGetters.push(makePromise);

return this;

}

/ 封裝 /

function LazyMan(name){

return new _LazyMan(name);

}

LazyMan("Hank").sleep(1).eat("dinner")更多技術資訊可關注:gzitcast

相關文章
相關標籤/搜索