中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

圖解 Promise 實現原理(一)—— 基礎實現

發布時間:2020-08-11 13:53:28 來源:ITPUB博客 閱讀:199 作者:vivo互聯網技術 欄目:web開發

本文首發于 vivo互聯網技術 微信公眾號 
鏈接: https://mp.weixin.qq.com/s/UNzYgpnKzmW6bAapYxnXRQ
作者:孔垂亮

很多同學在學習 Promise 時,知其然卻不知其所以然,對其中的用法理解不了。 本系列文章由淺入深逐步實現 Promise,并結合流程圖、實例以及動畫進行演示,達到深刻理解 Promise 用法的目的。

本系列文章有如下幾個章節組成:

  1. 圖解 Promise 實現原理(一)—— 基礎實現

  2. 圖解 Promise 實現原理(二)—— Promise 鏈式調用

  3. 圖解 Promise 實現原理(三)—— Promise 原型方法實現

  4. 圖解 Promise 實現原理(四)—— Promise 靜態方法實現

本文適合對 Promise 的用法有所了解的人閱讀,如果還不清楚,請自行查閱阮一峰老師的 《 ES6入門 之 Promise 對象》。

Promise 規范有很多,如 Promise/A,Promise/B,Promise/D 以及 Promise/A 的升級版 Promise/A+,有興趣的可以去了解下,最終 ES6 中采用了 Promise/A+ 規范。所以本文的Promise源碼是按照 Promise/A+規范來編寫的(不想看英文版的移步 Promise/A+規范中文翻譯)。

引子

為了讓大家更容易理解,我們從一個場景開始,一步一步跟著思路思考,會更容易看懂。

考慮下面一種獲取用戶 id 的請求處理:

//不使用Promise        http.get('some_url', function (result) {
    //do something
    console.log(result.id);
});//使用Promisenew Promise(function (resolve) {
    //異步請求
    http.get('some_url', function (result) {
        resolve(result.id)
    })
}).then(function (id) {
    //do something
    console.log(id);
})

圖解 Promise 實現原理(一)—— 基礎實現

乍一看,好像不使用 Promise 更簡潔一些。其實不然,設想一下,如果有好幾個依賴的前置請求都是異步的,此時如果沒有 Promise ,那回調函數要一層一層嵌套,看起來就很不舒服了。如下:

//不使用Promise        http.get('some_url', function (id) {
    //do something
    http.get('getNameById', id, function (name) {
        //do something
        http.get('getCourseByName', name, function (course) {
            //dong something
            http.get('getCourseDetailByCourse', function (courseDetail) {
                //do something
            })
        })
    })
});//使用Promisefunction getUserId(url) {
    return new Promise(function (resolve) {
        //異步請求
        http.get(url, function (id) {
            resolve(id)
        })
    })
}
getUserId('some_url').then(function (id) {
    //do something
    return getNameById(id); // getNameById 是和 getUserId 一樣的Promise封裝。下同}).then(function (name) {
    //do something
    return getCourseByName(name);
}).then(function (course) {
    //do something
    return getCourseDetailByCourse(course);
}).then(function (courseDetail) {
    //do something});
圖解 Promise 實現原理(一)—— 基礎實現實現原理

說到底,Promise 也還是使用回調函數,只不過是把回調封裝在了內部,使用上一直通過 then 方法的鏈式調用,使得多層的回調嵌套看起來變成了同一層的,書寫上以及理解上會更直觀和簡潔一些。

一、基礎版本

//極簡的實現class Promise {
    callbacks = [];
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {        this.callbacks.push(onFulfilled);
    }
    _resolve(value) {        this.callbacks.forEach(fn => fn(value));
    }
}//Promise應用let p = new Promise(resolve => {
    setTimeout(() => {
        console.log('done');
        resolve('5秒');
    }, 5000);
}).then((tip) => {
    console.log(tip);
})
圖解 Promise 實現原理(一)—— 基礎實現上述代碼很簡單,大致的邏輯是這樣的:
  1. 調用 then 方法,將想要在 Promise 異步操作成功時執行的 onFulfilled 放入callbacks隊列,其實也就是注冊回調函數,可以向觀察者模式方向思考;

  2. 創建 Promise 實例時傳入的函數會被賦予一個函數類型的參數,即 resolve,它接收一個參數 value,代表異步操作返回的結果,當異步操作執行成功后,會調用resolve方法,這時候其實真正執行的操作是將 callbacks 隊列中的回調一一執行。

圖解 Promise 實現原理(一)—— 基礎實現

(圖:基礎版本實現原理)

首先 new Promise 時,傳給 Promise 的函數設置定時器模擬異步的場景,接著調用 Promise 對象的 then 方法注冊異步操作完成后的 onFulfilled,最后當異步操作完成時,調用 resolve(value), 執行 then 方法注冊的 onFulfilled。

then 方法注冊的 onFulfilled 是存在一個數組中,可見 then 方法可以調用多次,注冊的多個onFulfilled 會在異步操作完成后根據添加的順序依次執行。如下:

//then 的說明let p = new Promise(resolve => {
    setTimeout(() => {
        console.log('done');
        resolve('5秒');
    }, 5000);
});
p.then(tip => {
    console.log('then1', tip);
});
p.then(tip => {
    console.log('then2', tip);
});

圖解 Promise 實現原理(一)—— 基礎實現上例中,要先定義一個變量 p ,然后 p.then 兩次。而規范中要求,then 方法應該能夠鏈式調用。 實現也簡單,只需要在 then 中 return this 即可。如下所示:

//極簡的實現+鏈式調用class Promise {
    callbacks = [];
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {        this.callbacks.push(onFulfilled);        return this;//看這里
    }
    _resolve(value) {        this.callbacks.forEach(fn => fn(value));
    }
}let p = new Promise(resolve => {
    setTimeout(() => {
        console.log('done');
        resolve('5秒');
    }, 5000);
}).then(tip => {
    console.log('then1', tip);
}).then(tip => {
    console.log('then2', tip);
});
圖解 Promise 實現原理(一)—— 基礎實現 圖解 Promise 實現原理(一)—— 基礎實現

(圖:基礎版本的鏈式調用)

二、加入延遲機制

上面 Promise 的實現存在一個問題:如果在 then 方法注冊 onFulfilled 之前,resolve 就執行了,onFulfilled 就不會執行到了。比如上面的例子中我們把 setTimout 去掉:

//同步執行了resolvelet p = new Promise(resolve => {
    console.log('同步執行');
    resolve('同步執行');
}).then(tip => {
    console.log('then1', tip);
}).then(tip => {
    console.log('then2', tip);
});

圖解 Promise 實現原理(一)—— 基礎實現

執行結果顯示,只有 "同步執行" 被打印了出來,后面的 "then1" 和 "then2" 均沒有打印出來。再回去看下 Promise 的源碼,也很好理解,resolve 執行時,callbacks 還是空數組,還沒有onFulfilled 注冊上來。

這顯然是不允許的,Promises/A+規范明確要求回調需要通過異步方式執行,用以保證一致可靠的執行順序。因此要加入一些處理,保證在 resolve 執行之前,then 方法已經注冊完所有的回調:

//極簡的實現+鏈式調用+延遲機制class Promise {
    callbacks = [];
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {        this.callbacks.push(onFulfilled);        return this;
    }
    _resolve(value) {
        setTimeout(() => {//看這里
            this.callbacks.forEach(fn => fn(value));
        });
    }
}
圖解 Promise 實現原理(一)—— 基礎實現在 resolve 中增加定時器,通過 setTimeout 機制,將 resolve 中執行回調的邏輯放置到JS任務隊列末尾,以保證在 resolve 執行時,then方法的 onFulfilled 已經注冊完成。

圖解 Promise 實現原理(一)—— 基礎實現

(圖:延遲機制)

但是這樣依然存在問題,在 resolve 執行后,再通過 then 注冊上來的 onFulfilled 都沒有機會執行了。如下所示,我們加了延遲后,then1 和 then2 可以打印出來了,但下例中的 then3 依然打印不出來。所以我們需要增加狀態,并且保存 resolve 的值。

let p = new Promise(resolve => {
    console.log('同步執行');
    resolve('同步執行');
}).then(tip => {
    console.log('then1', tip);
}).then(tip => {
    console.log('then2', tip);
});
setTimeout(() => {
    p.then(tip => {
        console.log('then3', tip);
    })
});

三、增加狀態

為了解決上一節拋出的問題,我們必須加入狀態機制,也就是大家熟知的 pending、fulfilled、rejected。

Promises/A+ 規范中明確規定了,pending 可以轉化為 fulfilled 或 rejected 并且只能轉化一次,也就是說如果 pending 轉化到 fulfilled 狀態,那么就不能再轉化到 rejected。并且 fulfilled 和 rejected 狀態只能由 pending 轉化而來,兩者之間不能互相轉換。

圖解 Promise 實現原理(一)—— 基礎實現

增加狀態后的實現是這樣的

//極簡的實現+鏈式調用+延遲機制+狀態class Promise {
    callbacks = [];
    state = 'pending';//增加狀態
    value = null;//保存結果
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {        if (this.state === 'pending') {//在resolve之前,跟之前邏輯一樣,添加到callbacks中
            this.callbacks.push(onFulfilled);
        } else {//在resolve之后,直接執行回調,返回結果了
            onFulfilled(this.value);
        }        return this;
    }
    _resolve(value) {        this.state = 'fulfilled';//改變狀態
        this.value = value;//保存結果
        this.callbacks.forEach(fn => fn(value));
    }
}

注意:當增加完狀態之后,原先的_resolve中的定時器可以去掉了。當reolve同步執行時,雖然callbacks為空,回調函數還沒有注冊上來,但沒有關系,因為后面注冊上來時,判斷狀態為fulfilled,會立即執行回調。

圖解 Promise 實現原理(一)—— 基礎實現 圖解 Promise 實現原理(一)—— 基礎實現

(圖:Promise 狀態管理)

實現源碼中只增加了 fulfilled 的狀態 和 onFulfilled 的回調,但為了完整性,在示意圖中增加了 rejected 和 onRejected 。后面章節會實現。

resolve 執行時,會將狀態設置為 fulfilled ,并把 value 的值存起來,在此之后調用 then 添加的新回調,都會立即執行,直接返回保存的value值。

(Promise 狀態變化演示動畫)

詳情請點擊: https://mp.weixin.qq.com/s/UNzYgpnKzmW6bAapYxnXRQ

至此,一個初具功能的Promise就實現好了,它實現了 then,實現了鏈式調用,實現了狀態管理等等。但仔細想想,鏈式調用的實現只是在 then 中 return 了 this,因為是同一個實例,調用再多次 then 也只能返回相同的一個結果,這顯然是不能滿足我們的要求的。下一節,講述如何實現真正的鏈式調用。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

敦煌市| 梓潼县| 景洪市| 克拉玛依市| 清涧县| 钟祥市| 保康县| 辽宁省| 盐亭县| 宁国市| 满城县| 汝南县| 赤峰市| 咸丰县| 丰镇市| 明溪县| 荥经县| 陵水| 安溪县| 海南省| 沧源| 湘乡市| 株洲县| 大渡口区| 临潭县| 鸡泽县| 梨树县| 溧阳市| 汤阴县| 瑞金市| 抚顺市| 唐山市| 博乐市| 平江县| 清新县| 西乌珠穆沁旗| 登封市| 进贤县| 五家渠市| 电白县| 山东|