動手寫乙個簡單的promise

2022-03-06 06:33:12 字數 4539 閱讀 3886

promise 是非同步程式設計的一種解決方案,比傳統的解決方案——**函式和事件——更合理和更強大。它由社群最早提出和實現,es6 將其寫進了語言標準,統一了用法,原生提供了promise物件。

所謂promise,簡單說就是乙個容器,裡面儲存著某個未來才會結束的事件(通常是乙個非同步操作)的結果。從語法上說,promise 是乙個物件,從它可以獲取非同步操作的訊息。promise 提供統一的 api,各種非同步操作都可以用同樣的方法進行處理。

promise物件有以下兩個特點。

(1)物件的狀態不受外界影響。promise物件代表乙個非同步操作,有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)。只有非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是promise這個名字的由來,它的英語意思就是「承諾」,表示其他手段無法改變。

(2)一旦狀態改變,就不會再變,任何時候都可以得到這個結果。promise物件的狀態改變,只有兩種可能:從pending變為fulfilled和從pending變為rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱為 resolved(已定型)。如果改變已經發生了,你再對promise物件新增**函式,也會立即得到這個結果。這與事件(event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。

注意,為了行文方便,本章後面的resolved統一只指fulfilled狀態,不包含rejected狀態。

有了promise物件,就可以將非同步操作以同步操作的流程表達出來,避免了層層巢狀的**函式。此外,promise物件提供統一的介面,使得控制非同步操作更加容易。

promise也有一些缺點。首先,無法取消promise,一旦新建它就會立即執行,無法中途取消。其次,如果不設定**函式,promise內部丟擲的錯誤,不會反應到外部。第三,當處於pending狀態時,無法得知目前進展到哪乙個階段(剛剛開始還是即將完成)。

根據上面的訊息和定義;我們先寫個簡單的,看著有點不像的mypromise;

function

mypromise(fn)

function

reject(value)

fn(resolve, reject);

}

現在你就可以用上自定義的promise了

new mypromise((resolve, reject) =>, 2000);

});//將會在2秒後輸出

解釋一下整體**:

mypromise 中的引數 fn是需要使用者傳入的自定義函式(該函式需要接收兩個引數)。

mypromise 中的內部還有兩個函式resolve和reject,其中 resolve 表示使用者的非同步任務成功時應該呼叫的函式,reject 表示使用者的非同步任務失敗時該呼叫的函式。

那使用者如何呼叫 resolve 和 reject 呢?

很簡單,把兩個函式當作 fn的引數傳遞出去即可。

所以 mypromise 內部在呼叫 fn 時會把 resolve 和 reject當作引數傳遞給 fn。

然後使用者在自定義函式內呼叫 resolve 或 reject 來通知 mypromise 非同步任務已經執行完了。

通過上面的**可以發現乙個問題,我們不知道promise的非同步任務進行到哪一步了、是成功還是失敗了。

所以增加三個狀態用來標識乙個promise的非同步任務進行到何種程度了。

pending、resolved、rejected 分別表示 執行中、已完成、已失敗。

然後通過觀察使用者呼叫的是 resolve 還是 reject 可以判斷當前promise的狀態。 那麼會有三種情況:

下面進行**的改造,定義了三個常量表示狀態以及乙個變數 state 用來儲存當前狀態。 並且當 resolve 被呼叫時將 state 修改為 resolved 。

const pedning="pending";//

執行狀態

const resolved='resolved';//

以完成;

const rejected='rejected';//

以失敗function

mypromise(fn)

function

reject(err)

fn(resolve,reject);

}

ok,現在已經能知道promise當前所處的狀態了,但是任務完了得拿到結果吧,mypromise 的 resolve 被呼叫,那也只是mypromise知道任務完成了,使用者還不知道呢。 所以我們需要**函式告訴使用者,是的,其實就是**函式。 這時候就輪到 then 方法出場了,使用者通過then方法傳入**函式, mypromise 將在成功呼叫 resolve 時呼叫使用者傳入的**函式。 開始改造**,mypromise 內部需要變數儲存**函式,then 方法的作用就是將使用者傳入的**函式賦予 mypromise 內的變數。 所以 then 方法長這樣,接收兩個引數,乙個是成功時的**函式,乙個是失敗時的**函式

const pedning="pending";//

執行狀態

const resolved='resolved';//

以完成;

const rejected='rejected';//

以失敗function

mypromise(fn)

function

reject(err)

fn(resolve,reject);

}mypromise.prototype.then=function

(onfulfilled,onrejected)

是的,乙個簡版promise幾乎大功告成,讓我們再試試在瀏覽器執行如下**(注意我刪除了 resolve 和 reject 裡的console語句);咱們來使用一下:

(function

()

function

reject(err)

fn(resolve,reject);

}mypromise.prototype.then=function

(onfulfilled,onrejected)

new mypromise((resolve,reject)=>,4000);

}).then((value)=>)

})()

通過匿名函式和函式自執行,形成區域性作用域,保護裡面的變數;

上面的**,用法上已經和promise長得差不多了,但是如果我們多次呼叫 then 方法呢? 是的,只有最後乙個 then 方法裡的**函式能執行,這當然沒法滿足我們的需要。 於是,將兩個**函式改成函式陣列(請回想一下前置知識),並在狀態更改時遍歷呼叫**函式。 改造後的**如下:

(function

()

function

reject(err)

fn(resolve,reject);

}mypromise.prototype.then=function

(onfulfilled,onrejected)

new mypromise((resolve,reject)=>,5000)

}).then((val)=>).then((val)=>)

})()

上面已經是簡版promise的實現了。 但是我們還可以更完善一點,增強 mypromise 的健壯性。 例如,若使用者自定義函式在執行過程中發生了錯誤,會中斷程式的執行,於是我們增加try...catch...語句,並在發生錯誤時主動執行reject函式告知使用者。

try

catch

(e)

又或者,對引數進行校驗,狀態進行判斷等,以 then為例,若使用者傳入的引數不是函式呢? 或者promise的狀態已經時rejected或resolved,此時呼叫then呢?

改造 then 後**如下:

mypromise.prototype.then = function

(onfulfilled, onrejected)

if(typeof onfulfilled !== 'function') ;

}const that = this

;

if (that.state ===pending)

if (that.state ===resolved)

if (that.state ===rejected)

}

手寫乙個Promise

js物件導向 在js中一切皆物件,但js並不是一種真正的物件導向 oop 的語言,因為它缺少類 class 的概念。雖然es6引入了class和extends,使我們能夠輕易地實現類和繼承。但js並不存在真實的類,js的類是通過函式以及原型鏈機制模擬的,本小節的就來 如何在es5環境下利用函式和原型...

手寫乙個promise

promise a 規範 注 以下 沒有通過promises aplus tests的全部測試,但基本功能還是全的 測試結果 864 passing,8 failing 另外可以參考這個指南中的 promise實現 promise resolve 100 規範 class mypromise con...

手寫系列 帶你實現乙個簡單的Promise

學習之前 需要先對promise有個基本了解哦,這裡都預設大家都是比較熟悉promise的 本次將帶小夥伴們實現promise的基本功能 promise的基本骨架 promise的then promise.then的多次呼叫 then鏈式呼叫 catch的實現 finally的實現 const pr...