亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

手寫一個(gè)符合A+規(guī)范的Promise

jsummer / 3511人閱讀

摘要:本文同時(shí)也發(fā)布在我的博客上,歡迎之前也手寫過(guò)簡(jiǎn)單的,這次則是為了通過(guò)官方的測(cè)試集,借鑒了一些下載量較多的,改了幾遍,終于是通過(guò)了規(guī)范的個(gè)測(cè)試用例如何測(cè)試測(cè)試庫(kù)地址在這,大家在寫完自己的后,不妨也去測(cè)試一下,檢驗(yàn)自己的是否符合規(guī)范。

本文同時(shí)也發(fā)布在我的github博客上,歡迎star~

之前也手寫過(guò)簡(jiǎn)單的promise,這次則是為了通過(guò)官方的Promise A+測(cè)試集,借鑒了一些下載量較多的promise polyfill,改了幾遍,終于是通過(guò)了A+規(guī)范的872個(gè)測(cè)試用例

如何測(cè)試?

測(cè)試庫(kù)地址在這:promises-tests ,大家在寫完自己的promise后,不妨也去測(cè)試一下,檢驗(yàn)自己的promise是否符合Promise A+規(guī)范。這個(gè)庫(kù)使用起來(lái)很方便,像下面這樣就可以了:

const tests = require("promises-aplus-tests");
const Promise = require("./index");

const deferred = function() {
    let resolve, reject;
    const promise = new Promise(function(_resolve, _reject) {
        resolve = _resolve;
        reject = _reject;
    });
    return {
        promise: promise,
        resolve: resolve,
        reject: reject
    };
};
const adapter = {
    deferred
};
tests.mocha(adapter);

其中,index.js中是你寫的Promise

實(shí)現(xiàn)

首先我們定義一些全局屬性:

const IS_ERROR = {};
let ERROR = null;

IS_ERROR作為發(fā)生錯(cuò)誤時(shí)的標(biāo)識(shí),ERROR用來(lái)保存錯(cuò)誤;

做好準(zhǔn)備工作,再來(lái)定義_Promise類,其中fn是Promise接受的函數(shù),構(gòu)造函數(shù)執(zhí)行時(shí)立刻調(diào)用;_status是Promise的狀態(tài),初始為0(pending),resolved時(shí)為1,rejected時(shí)為2;_value用來(lái)保存Promise resolved時(shí)的返回值和rejected時(shí)的失敗信息;_handlers用來(lái)保存Promise成功和失敗時(shí)調(diào)用的處理方法

function _Promise(fn) {
    this._status = 0;
    this._value = null;
    this._handlers = [];
    doFn(this, fn);
}

最后執(zhí)行doFn方法,傳入this值和fn:

function doFn(self, fn) {
    const ret = safeCallTwo(
        fn,
        function(value) {
            self.resolve(value);
        },
        function(reason) {
            self.reject(reason);
        }
    );
    if (ret === IS_ERROR) {
        self.reject(ERROR);
    }
}

其中safeCallTwo是用來(lái)安全執(zhí)行兩參數(shù)方法的函數(shù),當(dāng)執(zhí)行出錯(cuò)時(shí),捕獲錯(cuò)誤,保存在ERROR中,返回IS_ERROR標(biāo)識(shí):

function safeCallTwo(fn, arg1, arg2) {
    try {
        return fn(arg1, arg2);
    } catch (error) {
        ERROR = error;
        return IS_ERROR;
    }
}

doFn中,調(diào)用safeCallTwo,fn傳入兩個(gè)參數(shù)供我們調(diào)用,也就是我們常用的resolve方法和reject方法,并獲取到返回值,如果ret為錯(cuò)誤標(biāo)識(shí)IS_ERROR,則調(diào)用reject

_Promise原型上掛載著resolve和reject方法,如下:

_Promise.prototype.resolve = function(value) {
    if (this._status !== 0) {
        return;
    }
    this._status = 1;
    this._value = value;
    doThen(this);
};
_Promise.prototype.reject = function(reason) {
    if (this._status !== 0) {
        return;
    }
    this._status = 2;
    this._value = reason;
    doThen(this);
};

因?yàn)镻romise的狀態(tài)只能由pending轉(zhuǎn)為resolvedrejected,所以在執(zhí)行resolve和reject方法時(shí),要先判斷status是否為0,若不為0,直接return;修改status和value后,執(zhí)行doThen方法:

function doThen(self) {
    const handlers = self._handlers;
    handlers.forEach(handler => {
        doHandler(self, handler);
    });
}

doThen函數(shù)的作用是從self上取出的handlers并依次執(zhí)行

我們?cè)賮?lái)看一看掛載在原型上的then方法:

_Promise.prototype.then = function(onResolve, onReject) {
    const res = new _Promise(function() {});
    preThen(this, onResolve, onReject, res);
    return res;
};

我們知道,Promise是支持鏈?zhǔn)秸{(diào)用的,所以我們的then方法也會(huì)返回一個(gè)Promise,以供后續(xù)調(diào)用;

下面是preThen方法:

function preThen(self, onResolve, onReject, res) {
    onResolve = typeof onResolve === "function" ? onResolve : null;
    onReject = typeof onReject === "function" ? onReject : null;
    const handler = {
        onResolve,
        onReject,
        promise: res
    };
    if (self._status === 0) {
        self._handlers.push(handler);
        return;
    }
    doHandler(self, handler);
}

preThen方法接受4個(gè)值,分別為當(dāng)前Promise——self,resolve后的回調(diào)函數(shù)onResolve,reject后的回調(diào)函數(shù)onReject,then函數(shù)返回的promise——res。先判斷onResolve和onReject是否為函數(shù),若不是,直接置為null。再將onResolve、onReject、res放入handler對(duì)象中

接下來(lái)需要注意,Promise接受的函數(shù)(也就是上文的fn)并不是一定是異步調(diào)用resolve和reject,也有可能是同步的,也就是說(shuō)在執(zhí)行preThen函數(shù)時(shí),self的status可能已經(jīng)不為0了,這時(shí)候我們就不需要將handler保存起來(lái)等待調(diào)用,而是直接調(diào)用回調(diào)函數(shù)

doHandler函數(shù)代碼見下:

function doHandler(self, handler) {
    setTimeout(() => {
        const { onReject, onResolve, promise } = handler;
        const { _status, _value } = self;
        const handlerFun = _status === 1 ? onResolve : onReject;
        if (handlerFun === null) {
            _status === 1 ? promise.resolve(_value) : promise.reject(_value);
            return;
        }
        const ret = safeCallOne(handlerFun, _value);
        if (ret === IS_ERROR) {
            promise.reject(ERROR);
            return;
        }
        promise.resolve(ret);
    });
}

我們知道,即使是同步執(zhí)行relove或者reject,then函數(shù)接受的回調(diào)函數(shù)也不會(huì)立刻同步執(zhí)行,如下代碼會(huì)依次輸出1,3,2,而非1,2,3

const p = new Promise(resolve => {
    console.log(1);
    resolve();
});
p.then(() => {
    console.log(2);
});
console.log(3);

在這里,我使用了setTimeout來(lái)模擬這種模式,當(dāng)然,這只是一種粗糙的模擬,更好的方式是引入或?qū)崿F(xiàn)類似asap的庫(kù)(下個(gè)星期我可能會(huì)實(shí)現(xiàn)這個(gè),哈哈),但setTimeout也足夠通過(guò)測(cè)試了

doHandler函數(shù)中,我們調(diào)用相應(yīng)的回調(diào)函數(shù),需要注意的是,如果相應(yīng)回調(diào)函數(shù)為null(null是前文判斷回調(diào)函數(shù)不為function時(shí)統(tǒng)一賦值的),則直接調(diào)用then函數(shù)返回的promise的resolve或reject方法。

同樣,我們使用了safeCallOne來(lái)捕獲錯(cuò)誤,這里不再贅述

到這里,我們執(zhí)行測(cè)試,發(fā)現(xiàn)不出意外地沒有通過(guò),因?yàn)槲覀冎皇菍?shí)現(xiàn)了基礎(chǔ)的Promise,還沒有實(shí)現(xiàn)resolve中的thenable功能,下面是mdn對(duì)于thenable的描述:

返回一個(gè)狀態(tài)由給定value決定的Promise對(duì)象。如果該值是thenable(即,帶有then方法的對(duì)象),返回的Promise對(duì)象的最終狀態(tài)由then方法執(zhí)行決定;否則的話(該value為空,基本類型或者不帶then方法的對(duì)象),返回的Promise對(duì)象狀態(tài)為fulfilled,并且將該value傳遞給對(duì)應(yīng)的then方法。通常而言,如果你不知道一個(gè)值是否是Promise對(duì)象,使用Promise.resolve(value) 來(lái)返回一個(gè)Promise對(duì)象,這樣就能將該value以Promise對(duì)象形式使用

我們?cè)賮?lái)修改resolve方法:

_Promise.prototype.resolve = function(value) {
    if (this._status !== 0) {
        return;
    }
    if (this === value) {
        return this.reject(new TypeError("cant"s resolve itself"));
    }
    if (value && (typeof value === "function" || typeof value === "object")) {
        const then = getThen(value);
        if (then === IS_ERROR) {
            this.reject(ERROR);
            return;
        }
        if (value instanceof _Promise) {
            value.then(
                value => {
                    this.resolve(value);
                },
                reason => {
                    this.reject(reason);
                }
            );
            return;
        }
        if (typeof then === "function") {
            doFn(this, then.bind(value));
            return;
        }
    }
    this._status = 1;
    this._value = value;
    doThen(this);
};

先判斷this和value是否為一個(gè)Promise,若是一個(gè),則拋出錯(cuò)誤

再判斷value的類型是否為function或object,如果是,則實(shí)行g(shù)etThen方法進(jìn)行錯(cuò)誤捕獲:

function getThen(self) {
    try {
        return self.then;
    } catch (error) {
        ERROR = error;
        return IS_ERROR;
    }
}

若成功拿到then方法,檢測(cè)value instanceof _Promise,若為true,則直接采用value的狀態(tài)和value或者reason。

若then為function,則將then函數(shù)以value為this值,當(dāng)作fn執(zhí)行,也就是達(dá)成下面代碼的效果:

const p = new Promise(resolve => {
    resolve({
        then: _resolve => {
            _resolve(1);
        }
    });
});
p.then(value => console.log(value)); //打印1

我們?cè)俅螆?zhí)行測(cè)試,發(fā)現(xiàn)仍然有錯(cuò),其因出現(xiàn)在下面這種情況下:

const p = new _Promise(resolve => {
    resolve({
        then: _resolve => {
            setTimeout(() => _resolve(1)), 500;
        }
    });
    resolve(2);
});
p.then(value => console.log(value));

這個(gè)時(shí)候,使用我們的Promise,輸出的是2,而在規(guī)范中,應(yīng)當(dāng)是輸出1

原因是我們?cè)趯?duì)象的then方法中是異步地resolve,這個(gè)時(shí)候,下面的resolve(2)在執(zhí)行時(shí),status還沒有變,自然可以修改status和value

解決方法也很簡(jiǎn)單,只用在doFn方法中判斷是否為第一次執(zhí)行即可:

function doFn(self, fn) {
    let done = false;
    const ret = safeCallTwo(
        fn,
        function(value) {
            if (done) {
                return;
            }
            done = true;
            self.resolve(value);
        },
        function(reason) {
            if (done) {
                return;
            }
            done = true;
            self.reject(reason);
        }
    );
    if (ret === IS_ERROR) {
        if (done) {
            return;
        }
        done = true;
        self.reject(ERROR);
    }
}

再執(zhí)行測(cè)試,發(fā)現(xiàn)已經(jīng)測(cè)試用例全部通過(guò)~

代碼

完整代碼已放在我的github上,地址為https://github.com/Bowen7/playground/tree/master/promise-polyfill ,可以clone我的playground項(xiàng)目,再到promise-polyfill目錄下npm install,然后執(zhí)行npm test即可運(yùn)行測(cè)試

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/106646.html

相關(guān)文章

  • 手寫一款符合Promise/A+規(guī)范Promise

    摘要:手寫一款符合規(guī)范的長(zhǎng)篇預(yù)警有點(diǎn)長(zhǎng),可以選擇性觀看。初始狀態(tài)是,狀態(tài)可以有或者不能從轉(zhuǎn)換為或者從轉(zhuǎn)換成即只要由狀態(tài)轉(zhuǎn)換為其他狀態(tài)后,狀態(tài)就不可變更。 手寫一款符合Promise/A+規(guī)范的Promise 長(zhǎng)篇預(yù)警!有點(diǎn)長(zhǎng),可以選擇性觀看。如果對(duì)Promise源碼不是很清楚,還是推薦從頭看,相信你認(rèn)真從頭看到尾,并且去實(shí)際操作了,肯定會(huì)有收獲的。主要是代碼部分有點(diǎn)多,不過(guò)好多都是重復(fù)的,不...

    rubyshen 評(píng)論0 收藏0
  • 只會(huì)用就out了,手寫一個(gè)符合規(guī)范Promise

    摘要:傳入的回調(diào)函數(shù)也不是一個(gè)函數(shù)類型,那怎么辦規(guī)范中說(shuō)忽略它就好了。因此需要判斷一下回調(diào)函數(shù)的類型,如果明確是個(gè)函數(shù)再執(zhí)行它。 Promise是什么 所謂Promise,簡(jiǎn)單說(shuō)就是一個(gè)容器,里面保存著某個(gè)未來(lái)才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果。從語(yǔ)法上說(shuō),Promise 是一個(gè)對(duì)象,從它可以獲取異步操作的消息。Promise 提供統(tǒng)一的 API,各種異步操作都可以用同樣的方法進(jìn)行處...

    muzhuyu 評(píng)論0 收藏0
  • promise/A+規(guī)范翻譯以及手寫實(shí)現(xiàn)

    摘要:如果實(shí)現(xiàn)滿足所有要求,則實(shí)現(xiàn)可能允許。本條款允許使用特定于實(shí)現(xiàn)的方法來(lái)采用已知一致承諾的狀態(tài)。接下來(lái)根據(jù)規(guī)范進(jìn)行手寫實(shí)現(xiàn)注釋偷懶就將對(duì)應(yīng)的規(guī)范標(biāo)注出來(lái),其實(shí)基本上就是對(duì)著規(guī)范實(shí)現(xiàn)。 如果要手寫實(shí)現(xiàn)promise,那么先看看promise/A+規(guī)范,再來(lái)實(shí)現(xiàn),將會(huì)事半功倍。那么我先翻譯一下Promise/A+規(guī)范中的內(nèi)容。 術(shù)語(yǔ) 1.1 promise 是一個(gè)帶有符合此規(guī)范的the...

    LiuZh 評(píng)論0 收藏0
  • 手寫一個(gè)符合promise/A+規(guī)范promise庫(kù)

    摘要:使用及原理分析通過(guò)關(guān)鍵字創(chuàng)建實(shí)例接受一個(gè)參數(shù)方法返回兩個(gè)方法可用通過(guò)在方法中通過(guò)調(diào)用使成功或調(diào)用使失敗來(lái)控制狀態(tài)中可以執(zhí)行同步代碼也可以執(zhí)行異步代碼原型對(duì)象上有方法供實(shí)例調(diào)用方法接受兩個(gè)參數(shù)默認(rèn)為一個(gè)函數(shù)默認(rèn)為一個(gè)函數(shù)當(dāng)狀態(tài)為時(shí)執(zhí)行用戶傳入 promise使用及原理分析: 通過(guò)new關(guān)鍵字創(chuàng)建promise實(shí)例, 接受一個(gè)executor參數(shù), executor方法返回兩個(gè)方法 res...

    venmos 評(píng)論0 收藏0
  • JavaScript之手寫Promise

    摘要:如果狀態(tài)是等待態(tài)的話,就往回調(diào)函數(shù)中函數(shù),比如如下代碼就會(huì)進(jìn)入等待態(tài)的邏輯以上就是簡(jiǎn)單版實(shí)現(xiàn)實(shí)現(xiàn)一個(gè)符合規(guī)范的接下來(lái)大部分代碼都是根據(jù)規(guī)范去實(shí)現(xiàn)的。 為更好的理解, 推薦閱讀Promise/A+ 規(guī)范 實(shí)現(xiàn)一個(gè)簡(jiǎn)易版 Promise 在完成符合 Promise/A+ 規(guī)范的代碼之前,我們可以先來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)易版 Promise,因?yàn)樵诿嬖囍?,如果你能?shí)現(xiàn)出一個(gè)簡(jiǎn)易版的 Promise ...

    stefan 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<