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

資訊專欄INFORMATION COLUMN

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

jsummer / 3455人閱讀

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

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

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

如何測試?

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

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

實現(xiàn)

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

const IS_ERROR = {};
let ERROR = null;

IS_ERROR作為發(fā)生錯誤時的標識,ERROR用來保存錯誤;

做好準備工作,再來定義_Promise類,其中fn是Promise接受的函數(shù),構(gòu)造函數(shù)執(zhí)行時立刻調(diào)用;_status是Promise的狀態(tài),初始為0(pending),resolved時為1,rejected時為2;_value用來保存Promise resolved時的返回值和rejected時的失敗信息;_handlers用來保存Promise成功和失敗時調(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是用來安全執(zhí)行兩參數(shù)方法的函數(shù),當執(zhí)行出錯時,捕獲錯誤,保存在ERROR中,返回IS_ERROR標識:

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

doFn中,調(diào)用safeCallTwo,fn傳入兩個參數(shù)供我們調(diào)用,也就是我們常用的resolve方法和reject方法,并獲取到返回值,如果ret為錯誤標識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);
};

因為Promise的狀態(tài)只能由pending轉(zhuǎn)為resolvedrejected,所以在執(zhí)行resolve和reject方法時,要先判斷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í)行

我們再來看一看掛載在原型上的then方法:

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

我們知道,Promise是支持鏈式調(diào)用的,所以我們的then方法也會返回一個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個值,分別為當前Promise——self,resolve后的回調(diào)函數(shù)onResolve,reject后的回調(diào)函數(shù)onReject,then函數(shù)返回的promise——res。先判斷onResolve和onReject是否為函數(shù),若不是,直接置為null。再將onResolve、onReject、res放入handler對象中

接下來需要注意,Promise接受的函數(shù)(也就是上文的fn)并不是一定是異步調(diào)用resolve和reject,也有可能是同步的,也就是說在執(zhí)行preThen函數(shù)時,self的status可能已經(jīng)不為0了,這時候我們就不需要將handler保存起來等待調(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ù)也不會立刻同步執(zhí)行,如下代碼會依次輸出1,3,2,而非1,2,3

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

在這里,我使用了setTimeout來模擬這種模式,當然,這只是一種粗糙的模擬,更好的方式是引入或?qū)崿F(xiàn)類似asap的庫(下個星期我可能會實現(xiàn)這個,哈哈),但setTimeout也足夠通過測試了

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

同樣,我們使用了safeCallOne來捕獲錯誤,這里不再贅述

到這里,我們執(zhí)行測試,發(fā)現(xiàn)不出意外地沒有通過,因為我們只是實現(xiàn)了基礎(chǔ)的Promise,還沒有實現(xiàn)resolve中的thenable功能,下面是mdn對于thenable的描述:

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

我們再來修改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是否為一個Promise,若是一個,則拋出錯誤

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

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

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

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

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

我們再次執(zhí)行測試,發(fā)現(xiàn)仍然有錯,其因出現(xiàn)在下面這種情況下:

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

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

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

解決方法也很簡單,只用在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í)行測試,發(fā)現(xiàn)已經(jīng)測試用例全部通過~

代碼

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

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

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

相關(guān)文章

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

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

    rubyshen 評論0 收藏0
  • 只會用就out了,手寫一個符合規(guī)范Promise

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

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

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

    LiuZh 評論0 收藏0
  • 手寫一個符合promise/A+規(guī)范promise

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

    venmos 評論0 收藏0
  • JavaScript之手寫Promise

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

    stefan 評論0 收藏0

發(fā)表評論

0條評論

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