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

資訊專欄INFORMATION COLUMN

promise介紹--實(shí)現(xiàn)篇

shery / 2213人閱讀

摘要:內(nèi)部總體上分為兩種情況,一種是當(dāng)前對象狀態(tài)已經(jīng)變?yōu)榛?,此時(shí)則直接把響應(yīng)的回調(diào)函數(shù)添加到異步隊(duì)列中,另一種情況是當(dāng)前對象狀態(tài)還是,此時(shí)則把響應(yīng)的回調(diào)函數(shù)依次添加到數(shù)組中。

今天,我?guī)е蠹乙徊揭徊礁?guī)范實(shí)現(xiàn)一個(gè)自己的Promise,大家可以對照我的第二篇文章Promise介紹--規(guī)范篇或官方規(guī)范來一一學(xué)習(xí)。

Promise內(nèi)部有三個(gè)固定的狀態(tài),我們在文件中提前定義。

const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"

首先,是構(gòu)造器constructor

   constructor(resolver){
        this._status = PENDING; // 保存內(nèi)部的狀態(tài)
        this._result = undefined; // 保存promise對象fulfill或reject的最終結(jié)果 
        this._childArr = []; // 調(diào)用then方法創(chuàng)建的子promise對象
        this._fulfillArr = []; // 調(diào)用then方法添加的onFulfilled方法
        this._rejectArr = []; // 調(diào)用then方法添加的onRejected方法

        if (resolver == noop) { return}; // then方法內(nèi)部創(chuàng)建promise時(shí)間使用

        // 如果resolver不是函數(shù),則拋出TypeError錯(cuò)誤
        if (!isFunction(resolver)) {
            throw new TypeError("參數(shù)必須為function");
        };
        // 如果直接調(diào)用Promise而非通過new關(guān)鍵詞創(chuàng)建,同樣拋出TypeError錯(cuò)誤
        if (this instanceof Promise) {
            initPromise(this, resolver)
        } else {
            throw new TypeError("Promise不可以直接作為函數(shù)調(diào)用")
        }
    }

當(dāng)參數(shù)傳遞正確時(shí),才真正初始化Promise對象,我提到了一個(gè)多帶帶的函數(shù)initPromise中,如下:

function initPromise(promise, resolver){
    // 當(dāng)調(diào)用傳入的resolver函數(shù)拋出異常,則reject當(dāng)前promise
    try {
        resolver(function(value){
            // 封裝的內(nèi)部函數(shù),處理resolve(value),也就是Promise Resolution Procedure
            resolve(promise, value);
        }, function(reason){
            // 封裝的內(nèi)部函數(shù),處理reject(reason)
            reject(promise, reason);
        });
    } catch (e) {
        reject(promise, e);
    }
}

當(dāng)我們執(zhí)行new Promise((resolve){resolve(5)})時(shí),會(huì)走resolve(promise, value)。接下來我們實(shí)現(xiàn)一下Promise Resolution Procedure

function resolve(promise, value){
    // 2.3.1 如果promise和value指向同一對象
    if (promise === value) {
        reject(promise, new TypeError("不可以resolve Promise實(shí)例本身"))
    // 2.3.2 如果value是一個(gè)promise
    } else if (value instanceof Promise) {
        // 2.3.2.2 如果value處于fulfilled狀態(tài),則使用相同的value值fulfill promise。
        if (value._status == FULFILLED) {
            fulfill(promise, value._result);
        // 2.3.2.3 如果value處于rejected狀態(tài),則使用相同的reason值reject promise。
        } else if (value._status == REJECTED) {
            reject(promise, value._result);
        // 2.3.2.1 如果value處于pending狀態(tài),則promise同樣pending并直到value狀態(tài)改變。
        // 重新把resolve(promise, value)添加到隊(duì)列,asyncCall封裝了一下異步調(diào)用
        } else {
            asyncCall(resolve, [promise, value]);
        }
    // 2.3.3 如果x是一個(gè)object或function
    } else if (isObjectOrFunction(value)){
        // 2.3.3.2 如果獲取value.then的值時(shí)拋出異常,則通過該異常reject promise
        try{
            let then = value.then; // 2.3.3.1 使then等于value.then
            // 2.3.3.3 如果then是一個(gè)函數(shù)
            if (isFunction(then)) {
                try{
                    handleThenable(promise, value, then);
                } catch (e) {
                    reject(promise, e);
                }
            // 2.3.3.4 如果then不是一個(gè)函數(shù)
            } else {
                fulfill(promise, value);
            }
        } catch (e) {
            reject(promise, e);
        }
    // 2.3.4 value不是對象或函數(shù)
    } else {
        fulfill(promise, value);
    }
}

因?yàn)?b>value.then是函數(shù)時(shí),處理情況同樣很多且比較雜亂,我多帶帶把這部分處理提取到handleThenable函數(shù)中。實(shí)現(xiàn)如下:

function handleThenable(promise, value, then){
    let settled = false; // 是否fulfilled或rejected
    try {
        // 2.3.3.3 如果then是一個(gè)函數(shù),則把value作為函數(shù)中this指向來調(diào)用它
        then.call(value, (otherValue)=>{
            // 2.3.3.3.3 
            if (settled) { return};
            // 2.3.3.3.1 如果resolvePromise通過傳入y來調(diào)用,則執(zhí)行resolve(promise, y)
            resolve(promise, otherValue);
            settled = true;
        }, (reason)=>{
            // 2.3.3.3.3 
            if (settled) { return};
            // 2.3.3.3.2 如果rejectPromise 通過傳入原因r來調(diào)用,則傳入r來reject promise
            reject(promise, reason);
            settled = true;
        })
    // 2.3.3.3.4 如果調(diào)用then拋出異常e
    } catch (e) {
        // 2.3.3.3.4.1 如果resolvePromise或rejectPromise已經(jīng)調(diào)用,則忽略
        if (settled) { return};
        settled = true;
        // 2.3.3.3.4.2 否則,則傳入e來reject promise
        reject(promise, e)
    }
}

以上,基本就是完整的我對Promise Resolution Procedure的實(shí)現(xiàn)。
還有一個(gè)非常重要的方法就是then方法,接下來我們看一下它是如何實(shí)現(xiàn)的。then內(nèi)部總體上分為兩種情況,一種是當(dāng)前promise對象狀態(tài)已經(jīng)變?yōu)?b>fulfilled或rejected,此時(shí)則直接把響應(yīng)的回調(diào)函數(shù)添加到異步隊(duì)列中,另一種情況是當(dāng)前promise對象狀態(tài)還是pending,此時(shí)則把響應(yīng)的回調(diào)函數(shù)依次添加到數(shù)組中。

then(onFulfilled, onRejected){
    let child = new Promise(noop);
    // 如果當(dāng)前對象狀態(tài)已經(jīng)改變,則直接根據(jù)狀態(tài)調(diào)用響應(yīng)的回調(diào)
    if (this._status !== PENDING) {
        if (this._status == FULFILLED) {
            // 2.2.4 異步調(diào)用
            asyncCall(()=>{
                dealThen(this, child, onFulfilled);
            })
        } else {
            // 2.2.4 異步調(diào)用
            asyncCall(()=>{
                dealThen(this, child, onRejected);
            })
        }
    // 如果當(dāng)前對象處于pending狀態(tài),則把onFulfilled, onRejected添加到
    } else {
        this._childArr.push(child);
        this._fulfillArr.push(onFulfilled);
        this._rejectArr.push(onRejected);
    }
    // 返回一個(gè)新的promise對象
    return child;
}

具體處理邏輯我放到了一個(gè)新的函數(shù)dealThen中,注意它是異步調(diào)用的。所以用asyncCall方法包裝了一下。

// 處理then
function dealThen(promise, child, x){
    // onFulfilled或onRejected是一個(gè)函數(shù)
    if (isFunction(x)) {
        // 2.2.7.1 如果onFulfilled或onRejected返回了一個(gè)值value,則執(zhí)行resolve(child, value)
        try {
            resolve(child, x(promise._result));
        // 2.2.7.2 如果onFulfilled或onRejected拋出了異常e,則reject child并傳入原因e
        } catch (e) {
            reject(child, e);
        }
    } else {
        try{
            // 2.2.1.1 如果onFulfilled不是一個(gè)函數(shù),則忽略
            if (promise._status == FULFILLED) {
                fulfill(child, promise._result);
            // 2.2.1.2 如果onRejected不是一個(gè)函數(shù),則忽略
            } else {
                reject(child, promise._result);
            }
        } catch (e) {
            reject(child, e);
        }
    }
}

從上面的代碼中我們看到有兩個(gè)比較重要的方法——fulfillreject,它們才是真正改變promise狀態(tài)并調(diào)用相應(yīng)回調(diào)的地方。

function fulfill(promise, value){
    // 如果狀態(tài)已經(jīng)不是pending,則直接return
    if (promise._status !== PENDING) { return };
    // 設(shè)置狀態(tài)為fulfilled,并設(shè)置最終結(jié)果
    promise._status = FULFILLED;
    promise._result = value;
    // 異步依次調(diào)用添加的onFulfilled方法
    if (promise._fulfillArr.length > 0) {
        // 2.2.6.1 如果promise fulfilled,則所有的onFulfilled回調(diào)函數(shù)按照它們添加的順序依次調(diào)用。
        promise._fulfillArr.forEach((k,index)=>{
            // 2.2.5 onFulfilled和onRejected必須作為函數(shù)來調(diào)用,沒有this值
            asyncCall(dealThen, [promise, promise._childArr[index], k])
        });
    }
}

function reject(promise, reason){
    // 如果狀態(tài)已經(jīng)不是pending,則直接return
    if (promise._status !== PENDING) { return };

    // 設(shè)置狀態(tài)為rejected,并設(shè)置最終結(jié)果
    promise._status = REJECTED;
    promise._result = reason;

    // 異步依次調(diào)用添加的onRejected方法
    if (promise._rejectArr.length > 0) {
        // 2.2.6.2 如果promise rejected,則所有的onRejected回調(diào)函數(shù)按照它們添加的順序依次調(diào)用。
        promise._rejectArr.forEach((k,index)=>{
            // 2.2.5 onFulfilled和onRejected必須作為函數(shù)來調(diào)用,沒有this值
            asyncCall(dealThen, [promise, promise._childArr[index], k])
        });
    }
}

當(dāng)然,還有一個(gè)實(shí)例方法catch,其實(shí)它調(diào)用的也是then方法。

catch(onRejected){
    return this.then(undefined, onRejected);
}

當(dāng)然,Promise還有四個(gè)實(shí)例方法,分別如下:

resolve

Promise.resolve = function(value){
    return new Promise(function(resolve){resolve(value)})
}

reject

Promise.reject = function(reason){
    return new Promise(function(resolve, reject){reject(reason)})
}

allrace的實(shí)現(xiàn)沒有太好好思考,也沒有跑測試,不知道有沒有問題,而且個(gè)人感覺實(shí)現(xiàn)的也比較挫,是用setInterval一直輪詢查看每一個(gè)promise實(shí)例狀態(tài)有沒有改變,所以就不show code了。主要內(nèi)容還是講Promises/A+的實(shí)現(xiàn)。

完整的代碼見我的github。

當(dāng)然也有幾個(gè)問題,異步我是用setTimeout實(shí)現(xiàn)的,它屬于macro-task,而原生的Promise屬于micro-task,所以這里還有待改進(jìn)。

另外,在上面的實(shí)現(xiàn)中,我們發(fā)現(xiàn)resolve(promise, value)中,在對2.3.2.1 如果value處于pending狀態(tài),則promise同樣pending并直到value狀態(tài)改變。我基本采用的也是setTimeout
輪詢的方式實(shí)現(xiàn)的。之后看了es-promise的實(shí)現(xiàn),因?yàn)楦鶕?jù)2.3.2.1此時(shí)promise的狀態(tài)和value是同步的,所以可以把resolvereject promise分別放在value相應(yīng)狀態(tài)的回調(diào)中,并假設(shè)此時(shí)與之對應(yīng)的valuechildundefined。如下所示:

// asyncCall(resolve, [promise, value]);
value._childArr.push(undefined);
value._fulfillArr.push((value)=>{resolve(promise, value)}); // ①
value._rejectArr.push((reason)=>{reject(promise, reason)}); // ②

此時(shí)我們還需要對fulfillreject兩個(gè)方法稍作改動(dòng)。

function fulfill(promise, value){
    if (promise._status !== PENDING) { return };

    promise._status = FULFILLED;
    promise._result = value;

    if (promise._fulfillArr.length > 0) {
        promise._fulfillArr.forEach((k,index)=>{
            // 如果對應(yīng)的child不是undefined,則異步調(diào)用回調(diào)
            if (promise._childArr[index]) {
                asyncCall(dealThen, [promise, promise._childArr[index], k])
            // 如果對應(yīng)的child是undefined,則直接執(zhí)行回調(diào)函數(shù)①并傳入value
            } else {
                k(value);
            }
        });
    }
}

function reject(promise, reason){
    if (promise._status !== PENDING) { return };

    promise._status = REJECTED;
    promise._result = reason;

    if (promise._rejectArr.length > 0) {
        promise._rejectArr.forEach((k,index)=>{
            // 如果對應(yīng)的child不是undefined,則異步調(diào)用回調(diào)
            if (promise._childArr[index]) {
                asyncCall(dealThen, [promise, promise._childArr[index], k])
            // 如果對應(yīng)的child是undefined,則直接執(zhí)行回調(diào)函數(shù)②并傳入reason
            } else {
                k(reason);
            }
        });
    };
    
}

修改版見Promise1。

錯(cuò)誤或不足之處,歡迎指正。

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

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

相關(guān)文章

  • Promise介紹--Deferred及jQuery

    摘要:我們稱為回調(diào)對象,它內(nèi)部會(huì)維護(hù)一個(gè)數(shù)組,我們可以向其中添加若干個(gè)回調(diào)函數(shù),然后在某一條件下觸發(fā)執(zhí)行。第一次之后,再次新的回調(diào)函數(shù)時(shí),自動(dòng)執(zhí)行回調(diào)。當(dāng)前面的回調(diào)函數(shù)返回時(shí),終止后面的回調(diào)繼續(xù)執(zhí)行。 最近懶癌發(fā)作,說好的系列文章,寫了一半,一直懶得寫,今天補(bǔ)上一篇。 Deferred 我們在使用promise對象時(shí),總會(huì)提到一個(gè)與它關(guān)系密切的對象——Deferred。其實(shí)Deferred沒...

    Darkgel 評論0 收藏0
  • Promise介紹--規(guī)范

    摘要:規(guī)范中對于構(gòu)造函數(shù)沒有明確說明,所以在此處拿出來講解一下。構(gòu)造函數(shù)只接收一個(gè)參數(shù),且該參數(shù)必須是一個(gè)函數(shù),任何其他的值比如等都會(huì)報(bào)一個(gè)的錯(cuò)誤。 本篇文章是Promise系列文章的第二篇,主要是講解基于Promise/A+規(guī)范,在傳入不同類型的參數(shù)時(shí),promise內(nèi)部分別會(huì)如何處理。本章的主要目的是讓大家對promise有一個(gè)更加深入的理解,也為下一篇講如何實(shí)現(xiàn)一個(gè)promise庫做準(zhǔn)...

    tylin 評論0 收藏0
  • promise介紹--基礎(chǔ)

    摘要:請求的傳統(tǒng)寫法改為后的寫法很顯然,我們把異步中使用回調(diào)函數(shù)的場景改為了等函數(shù)鏈?zhǔn)秸{(diào)用的方式。數(shù)組中第一個(gè)元素是異步的,第二個(gè)是非異步,會(huì)立即改變狀態(tài),所以新對象會(huì)立即改變狀態(tài)并把傳遞給成功時(shí)的回調(diào)函數(shù)。 前言 Promise,相信每一個(gè)前端工程師都或多或少地在項(xiàng)目中都是用過,畢竟它早已不是一個(gè)新名詞。ES6中已經(jīng)原生對它加以支持,在caniuse中搜索一下Promise,發(fā)現(xiàn)新版的ch...

    tanglijun 評論0 收藏0
  • ES6-7

    摘要:的翻譯文檔由的維護(hù)很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書的目的是以目前還在制定中的ECMASc...

    mudiyouyou 評論0 收藏0
  • 小而美的Promise庫——promiz源碼淺析

    摘要:因此,當(dāng)作為參數(shù)的執(zhí)行任意結(jié)果的回調(diào)函數(shù)時(shí),就會(huì)將參數(shù)傳遞給外層的,執(zhí)行對應(yīng)的回調(diào)函數(shù)。 背景 在上一篇博客[[譯]前端基礎(chǔ)知識(shí)儲(chǔ)備——Promise/A+規(guī)范](https://segmentfault.com/a/11...,我們介紹了Promise/A+規(guī)范的具體條目。在本文中,我們來選擇了promiz,讓大家來看下一個(gè)具體的Promise庫的內(nèi)部代碼是如何運(yùn)作的。 promiz...

    figofuture 評論0 收藏0

發(fā)表評論

0條評論

shery

|高級講師

TA的文章

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