成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

從零實(shí)現(xiàn)一個簡易的 Promise

WilsonLiu95 / 1411人閱讀

從零實(shí)現(xiàn)一個簡易的 Promise
所有問題都可以通過加一層中間層來解決。

Promises/A+

簡易的,不做廢話直接開始 :)

const p = new Promise((resolve, reject)=>{ 
    // 如果操作成功則調(diào)用 resolve 并傳入 value
    // 如果操作失敗則調(diào)用 reject 并傳入 reason
});

通常我們都會使用上述方法獲取 Promise 實(shí)例:在構(gòu)造函數(shù)種傳入一個 executor 方法,當(dāng)同步/異步的任務(wù)完成時調(diào)用 resolve,失敗時調(diào)用 reject,簡單易懂,在此不多贅述。

狀態(tài)機(jī)

一個 Promise 可以理解為一個狀態(tài)機(jī),相應(yīng)的 API 接口要么用于改變狀態(tài)機(jī)的狀態(tài),要么在到達(dá)某個狀態(tài)時被觸發(fā),因此首先需要實(shí)現(xiàn)的是 Promise 的狀態(tài)信息:

const PENDING = 0
const FULFILLED = 1
const REJECTED = 2

并且只存在 PENDING => FULFILLED 或者 PENDING => REJECTED 的狀態(tài)轉(zhuǎn)移。

構(gòu)造函數(shù)

首先實(shí)現(xiàn)構(gòu)造函數(shù)的框架如下:

class Promise {
    constructor(executor) {
        this.status = PENDING; // 實(shí)例當(dāng)前的狀態(tài)
        this.data = undefined; // Promise 返回的值
        this.defered = []; // 回調(diào)函數(shù)集
        executor(resolve, reject); // 執(zhí)行 executor 并傳入相應(yīng)的參數(shù)
    }
}

上述代碼基本實(shí)現(xiàn) Promise 構(gòu)造函數(shù)的主題部分,但是存在三個問題:

resolve 和 reject 參數(shù)/方法尚未定義

executor 函數(shù)體中可能會拋出異常,需要做容錯處理

考慮 executor 函數(shù)體中在調(diào)用 resolve/reject 時的 this 指向問題

修修補(bǔ)補(bǔ)如下:

class Promise {
    constructor(executor) {
        this.status = PENDING;
        this.data = undefined;
        this.defered = [];

        try {
            // bind, bind, bind!
            executor(this.resolve.bind(this), this.reject.bind(this));
        } catch (e) {
            this.reject(e);
        }
    }

    resolve(value) {
        // TODO
    }

    reject(reason) {
        // TODO
    }
}
resolve & reject

接下來實(shí)現(xiàn) resolve 和 reject 方法,基本上就是在判斷狀態(tài)為 PENDING 之后把狀態(tài)改為相應(yīng)的值,并把對應(yīng)的 value 和 reason 存在 self 的 data 屬性上面,最后執(zhí)行相應(yīng)的回調(diào)函數(shù),邏輯很簡單:

resolve(value) {
    if (this.status === PENDING) {
        this.status = FULFILLED;
        this.data = value;
        this.defered.forEach(i => i.onfulfiled(value));
    }
}

reject(reason) {
    if (this.status === PENDING) {
        this.status = REJECTED;
        this.data = reason;
        this.defered.forEach(i => i.onrejected(reason));
    }
}
then 方法

Promise 對象有一個 then 方法,用來注冊在這個 Promise 狀態(tài)確定后的回調(diào),很明顯 then 方法需要寫在原型鏈上,Promise 總共有三種可能的狀態(tài),在 then 方法中我們分別用三個判斷分支來處理,并且都分別返回一個新的 Promise 實(shí)例。

then(onResolved, onRejected) {
    // 如果 then 的參數(shù)不是 function 則我們需要忽略它
    onResolved = typeof onResolved === "function" ? onResolved : function(v) {};
    onRejected = typeof onRejected === "function" ? onRejected : function(r) {};

    switch (this.status) {
        case FULFILLED:
            return new Promise((resolve, reject) => {
                // TODO
            });

        case REJECTED:
            return new Promise((resolve, reject) => {
                // TODO
            });

        case PENDING:
            return new Promise((resolve, reject) => {
                // TODO
            });
    }
}

完整的實(shí)現(xiàn)如下,其中需要注意的是,如果 onResolved 的返回值是一個 Promise 對象,則直接取它的結(jié)果做為新的 Promise 實(shí)例的結(jié)果

then(onResolved, onRejected) {
    onResolved = typeof onResolved === "function" ? onResolved : function(v) {};
    onRejected = typeof onRejected === "function" ? onRejected : function(r) {};

    switch (this.status) {
        case FULFILLED:
            return new Promise((resolve, reject) => {
                try {
                    const r = onResolved(this.data);
                    r instanceof Promise && r.then(resolve, reject);
                    resolve(r);
                } catch (e) {
                    reject(e);
                }
            });

        case REJECTED:
            return new Promise((resolve, reject) => {
                try {
                    const r = onRejected(this.data);
                    r instanceof Promise && r.then(resolve, reject);
                } catch (e) {
                    reject(e);
                }
            });

        case PENDING:
            return new Promise((resolve, reject) => {
                const onfulfiled = () => {
                    try {
                        const r = onResolved(this.data);
                        r instanceof Promise && r.then(resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                };

                const onrejected = () => {
                    try {
                        const r = onRejected(this.data);
                        r instanceof Promise && r.then(resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                };

                this.defered.push({
                    onfulfiled,
                    onrejected
                });
            });
    }
}

至此實(shí)現(xiàn)一個簡易的 Promise,使用如下測試用例驗(yàn)證:

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1);
    }, 1000);
}).then((res) => {
    console.log(res);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
        }, 1000);
    });
}).then((res) => {
    console.log(res);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
        }, 1000);
    });
}).then((res) => {
    console.log(res);
});

// 1
// 2
// 3
// [Finished in 3.1s]
其他問題(UPDATED) 異步問題
new Promise((resolve) => {
        resolve();
    })
    .then(() => {
        console.log("1");
    })
    .then(() => {
        console.log("2");
    });

console.log("3");

執(zhí)行上面的代碼會發(fā)現(xiàn)輸出的順序是“1, 2, 3”,而不是正確的“3, 1, 2”,顯然是因?yàn)槲覀儧]有在 Promise 的 resolve 方法中異步的調(diào)用回調(diào)函數(shù)集導(dǎo)致的,當(dāng)然解決這個問題也很簡單,就是使用 setTimeout,但是這樣實(shí)現(xiàn)的話并不符合 Promise 在事件循環(huán)中的優(yōu)先級,所以暫時忽略。

值穿透問題
new Promise((resolve) => {
        resolve(8);
    })
    .then()
    .then()
    .then((value) => {
        console.log(value)
    });

上面的代碼使用我們剛剛實(shí)現(xiàn)的 Promise 會打印 undefined,然而這并不是我們期望得到的結(jié)果,我們希望的是8這個值會穿過兩個 then 到達(dá)鏈尾的 then 的執(zhí)行函數(shù)里,其輸出應(yīng)該和這段代碼一致:

new Promise((resolve) => {
        resolve(8);
    })
    .then((value) => {
        return value;
    })
    .then((value) => {
        return value;
    })
    .then((value) => {
        console.log(value);
    });

其實(shí)要實(shí)現(xiàn)這個功能十分簡單,只要把 then 的兩個參數(shù)的默認(rèn)值做簡單的修改:

onResolved = typeof onResolved === "function" ? onResolved : function(v) { return v; };
onRejected = typeof onRejected === "function" ? onRejected : function(r) { return r; };
Promise 就是充當(dāng)一個中間層,把回調(diào)造成的控制反轉(zhuǎn)再反轉(zhuǎn)回去。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/94843.html

相關(guān)文章

  • 從零開始實(shí)現(xiàn)一個簡易Java MVC框架

    摘要:不過仔細(xì)了解了一段時候發(fā)現(xiàn),其實(shí)他的原理是很簡單的,所以想要自己也動手實(shí)現(xiàn)一個功能類似的框架。原文地址從零開始實(shí)現(xiàn)一個簡易的框架 前言 最近在看spring-boot框架的源碼,看了源碼之后更是讓我感受到了spring-boot功能的強(qiáng)大。而且使用了很多的設(shè)計模式,讓人在看的時候覺得有點(diǎn)難以下手。 不過仔細(xì)了解了一段時候發(fā)現(xiàn),其實(shí)他的原理是很簡單的,所以想要自己也動手實(shí)現(xiàn)一個功能類似的...

    neuSnail 評論0 收藏0
  • 從零開始實(shí)現(xiàn)一個簡易Java MVC框架(五)--引入aspectj實(shí)現(xiàn)AOP切點(diǎn)

    摘要:接下來就可以把這個切點(diǎn)類加入到我們之前實(shí)現(xiàn)的功能中了。實(shí)現(xiàn)的切點(diǎn)功能首先改裝注解,把之前改成來存儲表達(dá)式。測試用例在上一篇文章從零開始實(shí)現(xiàn)一個簡易的框架四實(shí)現(xiàn)中的測試用例的基礎(chǔ)上修改測試用例。 前言 在上一節(jié)從零開始實(shí)現(xiàn)一個簡易的Java MVC框架(四)--實(shí)現(xiàn)AOP中我們實(shí)現(xiàn)了AOP的功能,已經(jīng)可以生成對應(yīng)的代理類了,但是對于代理對象的選擇只能通過指定的類,這樣確實(shí)不方便也不合理。...

    wupengyu 評論0 收藏0
  • 從零開始實(shí)現(xiàn)一個簡易Java MVC框架(六)--加強(qiáng)AOP功能

    摘要:在前面的文章中實(shí)現(xiàn)的功能時,目標(biāo)類都只能被一個切面代理,如果想要生成第二個代理類,就會把之前的代理類覆蓋。改裝原有功能現(xiàn)在要改裝原來的的實(shí)現(xiàn)代碼,讓的功能加入到框架中為了讓切面能夠排序,先添加一個注解,用于標(biāo)記排序。 前言 在前面從零開始實(shí)現(xiàn)一個簡易的Java MVC框架(四)--實(shí)現(xiàn)AOP和從零開始實(shí)現(xiàn)一個簡易的Java MVC框架(五)--引入aspectj實(shí)現(xiàn)AOP切點(diǎn)這兩節(jié)文章...

    Loong_T 評論0 收藏0
  • 從零開始實(shí)現(xiàn)一個簡易Java MVC框架(八)--制作Starter

    摘要:服務(wù)器相關(guān)配置啟動類資源目錄目錄靜態(tài)文件目錄端口號目錄目錄實(shí)現(xiàn)內(nèi)嵌服務(wù)器在上一章文章從零開始實(shí)現(xiàn)一個簡易的框架七實(shí)現(xiàn)已經(jīng)在文件中引入了依賴,所以這里就不用引用了。 spring-boot的Starter 一個項(xiàng)目總是要有一個啟動的地方,當(dāng)項(xiàng)目部署在tomcat中的時候,經(jīng)常就會用tomcat的startup.sh(startup.bat)的啟動腳本來啟動web項(xiàng)目 而在spring-b...

    AprilJ 評論0 收藏0
  • 從零開始實(shí)現(xiàn)一個簡易Java MVC框架(九)--優(yōu)化MVC代碼

    摘要:前言在從零開始實(shí)現(xiàn)一個簡易的框架七實(shí)現(xiàn)中實(shí)現(xiàn)了框架的的功能,不過最后指出代碼的邏輯不是很好,在這一章節(jié)就將這一部分代碼進(jìn)行優(yōu)化。 前言 在從零開始實(shí)現(xiàn)一個簡易的Java MVC框架(七)--實(shí)現(xiàn)MVC中實(shí)現(xiàn)了doodle框架的MVC的功能,不過最后指出代碼的邏輯不是很好,在這一章節(jié)就將這一部分代碼進(jìn)行優(yōu)化。 優(yōu)化的目標(biāo)是1.去除DispatcherServlet請求分發(fā)器中的http邏...

    ruicbAndroid 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<