一道題解:前端鏈式呼叫和事件迴圈

趙帥強發表於2022-03-16

一道題目

看到一個很有趣的題目:實現一個方法,支援鏈式呼叫。

lazyman.lazy('Lisa').sleep(3).sleepFirst(4).eat('lunch');
// 4s後輸出:Sleep Afater 4
// 輸出:I'm Lisa
// 3s後輸出:Sleep After 3
// 輸出:I'm eat lunch

解法

話不多說,直接上程式碼:

class LazyMan {
    callbacks = [];

    constructor() {
        this.next();
    }

    next() {
        setTimeout(() => {
            const firstFn = this.callbacks.shift();
            firstFn && firstFn();
        }, 0);
    }

    lazy(name) {
        this.callbacks.push(() => {
            console.log(`Hi, I'm ${name}`);
            this.next();
        });
        return this;
    }

    sleep(time) {
        this.callbacks.push(() => {
            setTimeout(() => {
                console.log(`Sleep after ${time}`);
                this.next();
            }, time * 1000);
        });
        return this;
    }

    sleepFirst(time) {
        this.callbacks.unshift(() => {
            setTimeout(() => {
                console.log(`Sleep after ${time}`);
                this.next();
            }, time * 1000);
        });
        return this;
    }

    eat(item) {
        this.callbacks.push(() => {
            console.log(`I am eat ${item}`);
            this.next();
        });
        return this;
    }
}

const lazyman = new LazyMan();
lazyman.lazy('Lisa').sleep(3).sleepFirst(4).eat('lunch');

題解分析

這個題目,首先要知道如何完成鏈式呼叫,就是設定一個類,類中宣告的方法的結尾最後都會重新返回該類例項的引用,這樣就能夠鏈式呼叫了。

所以我們建立一個LazyMan的類。

接下來是如何保證順序執行,那就是使用一個回撥陣列,每個函式呼叫後會檢查一次回撥是否為空,如果不為空,則繼續執行。
同時為了保證第一次執行前,會先進行一遍所有函式的遍歷,確認優先順序,我們在constructor裡面使用setTimeout進行建立一個微任務,這樣會等main函式裡的巨集任務全部執行完,才會開始真正的函式執行。

所以這個題目的關鍵點:

  • callbacks: 儲存函式執行的列表,方便調整執行順序。
  • class:建立一個類,為每個函式提供統一的父親,通過return this保證鏈式呼叫。
  • next:分步執行函式,保證確認好順序後,所有的函式都會被依次執行。
  • setTimeout:

    • 定時器。
    • 建立一個微任務,保證在main函式遍歷後再開始執行。

相關文章