Promise實現原理(附原始碼)
本篇文章主要在於探究
Promise
的實現原理,帶領大家一步一步實現一個Promise
, 不對其用法做說明,如果讀者還對Promise的用法不瞭解,可以檢視阮一峰老師的ES6 Promise教程。
接下來,帶你一步一步實現一個 Promise
1. Promise
基本結構
123456 | newPromise((resolve,reject)=>{setTimeout(()=>{resolve('FULFILLED')},1000)}) |
建構函式
Promise
必須接受一個函式作為引數,我們稱該函式為handle
,handle
又包含resolve
和reject
兩個引數,它們是兩個函式。
定義一個判斷一個變數是否為函式的方法,後面會用到
123 | // 判斷變數否為functionconstisFunction=variable=>typeof variable==='function' |
首先,我們定義一個名為 MyPromise
的 Class
,它接受一個函式 handle
作為引數
12345678 | classMyPromise{constructor(handle){if(!isFunction(handle)){thrownewError('MyPromise must accept a function as a parameter')}}} |
再往下看
2. Promise
狀態和值
Promise
物件存在以下三種狀態:
Pending(進行中)
Fulfilled(已成功)
Rejected(已失敗)
狀態只能由
Pending
變為Fulfilled
或由Pending
變為Rejected
,且狀態改變之後不會在發生變化,會一直保持這個狀態。
Promise
的值是指狀態改變時傳遞給回撥函式的值
上文中
handle
函式包含resolve
和reject
兩個引數,它們是兩個函式,可以用於改變Promise
的狀態和傳入Promise
的值
123456 | newPromise((resolve,reject)=>{setTimeout(()=>{resolve('FULFILLED')},1000)}) |
這裡 resolve
傳入的 "FULFILLED"
就是 Promise
的值
resolve
和 reject
resolve
: 將Promise物件的狀態從Pending(進行中)
變為Fulfilled(已成功)
reject
: 將Promise物件的狀態從Pending(進行中)
變為Rejected(已失敗)
resolve
和reject
都可以傳入任意型別的值作為實參,表示Promise
物件成功(Fulfilled)
和失敗(Rejected)
的值
瞭解了 Promise
的狀態和值,接下來,我們為 MyPromise
新增狀態屬性和值
首先定義三個常量,用於標記Promise物件的三種狀態
12345 | // 定義Promise的三種狀態常量constPENDING='PENDING'constFULFILLED='FULFILLED'constREJECTED='REJECTED' |
再為
MyPromise
新增狀態和值,並新增狀態改變的執行邏輯
123456789101112131415161718192021222324252627282930 | classMyPromise{constructor(handle){if(!isFunction(handle)){thrownewError('MyPromise must accept a function as a parameter')}// 新增狀態this._status=PENDING// 新增狀態this._value=undefined// 執行handletry{handle(this._resolve.bind(this),this._reject.bind(this))}catch(err){this._reject(err)}}// 新增resovle時執行的函式_resolve(val){if(this._status!==PENDING)returnthis._status=FULFILLEDthis._value=val}// 新增reject時執行的函式_reject(err){if(this._status!==PENDING)returnthis._status=REJECTEDthis._value=err}} |
這樣就實現了 Promise
狀態和值的改變。下面說一說 Promise
的核心: then
方法
3. Promise
的 then
方法
Promise
物件的 then
方法接受兩個引數:
12 | promise.then(onFulfilled,onRejected) |
引數可選
onFulfilled
和 onRejected
都是可選引數。
- 如果
onFulfilled
或onRejected
不是函式,其必須被忽略
onFulfilled
特性
如果 onFulfilled
是函式:
- 當
promise
狀態變為成功時必須被呼叫,其第一個引數為promise
成功狀態傳入的值(resolve
執行時傳入的值) - 在
promise
狀態改變前其不可被呼叫 - 其呼叫次數不可超過一次
onRejected
特性
如果 onRejected
是函式:
- 當
promise
狀態變為失敗時必須被呼叫,其第一個引數為promise
失敗狀態傳入的值(reject
執行時傳入的值) - 在
promise
狀態改變前其不可被呼叫 - 其呼叫次數不可超過一次
多次呼叫
then
方法可以被同一個 promise
物件呼叫多次
- 當
promise
成功狀態時,所有onFulfilled
需按照其註冊順序依次回撥 - 當
promise
失敗狀態時,所有onRejected
需按照其註冊順序依次回撥
返回
then
方法必須返回一個新的 promise
物件
12 | promise2=promise1.then(onFulfilled,onRejected); |
因此 promise
支援鏈式呼叫
12 | promise1.then(onFulfilled1,onRejected1).then(onFulfilled2,onRejected2); |
這裡涉及到 Promise
的執行規則,包括“值的傳遞”和“錯誤捕獲”機制:
1、如果 onFulfilled
或者 onRejected
返回一個值 x
,則執行下面的 Promise
解決過程:[[Resolve]](promise2, x)
- 若
x
不為Promise
,則使x
直接作為新返回的Promise
物件的值, 即新的onFulfilled
或者onRejected
函式的引數. - 若
x
為Promise
,這時後一個回撥函式,就會等待該Promise
物件(即x
)的狀態發生變化,才會被呼叫,並且新的Promise
狀態和x
的狀態相同。
下面的例子用於幫助理解:
12345678910111213 | let promise1=newPromise((resolve,reject)=>{setTimeout(()=>{resolve()},1000)})promise2=promise1.then(res=>{// 返回一個普通值return'這裡返回一個普通值'})promise2.then(res=>{console.log(res)//1秒後打印出:這裡返回一個普通值}) |
1234567891011121314151617 | let promise1=newPromise((resolve,reject)=>{setTimeout(()=>{resolve()},1000)})promise2=promise1.then(res=>{// 返回一個Promise物件returnnewPromise((resolve,reject)=>{setTimeout(()=>{resolve('這裡返回一個Promise')},2000)})})promise2.then(res=>{console.log(res)//3秒後打印出:這裡返回一個Promise}) |
2、如果 onFulfilled
或者onRejected
丟擲一個異常 e
,則 promise2
必須變為失敗(Rejected)
,並返回失敗的值 e
,例如:
1234567891011121314 | let promise1=newPromise((resolve,reject)=>{setTimeout(()=>{resolve('success')},1000)})promise2=promise1.then(res=>{thrownewError('這裡丟擲一個異常e')})promise2.then(res=>{console.log(res)},err=>{console.log(err)//1秒後打印出:這裡丟擲一個異常e}) |
3、如果onFulfilled
不是函式且 promise1
狀態為成功(Fulfilled)
, promise2
必須變為成功(Fulfilled)
並返回 promise1
成功的值,例如:
123456789101112 | let promise1=newPromise((resolve,reject)=>{setTimeout(()=>{resolve('success')},1000)})promise2=promise1.then('這裡的onFulfilled本來是一個函式,但現在不是')promise2.then(res=>{console.log(res)// 1秒後打印出:success},err=>{console.log(err)}) |
4、如果 onRejected
不是函式且 promise1
狀態為失敗(Rejected)
,promise2
必須變為失敗(Rejected)
並返回 promise1
失敗的值,例如:
123456789101112 | let promise1=newPromise((resolve,reject)=>{setTimeout(()=>{reject('fail')},1000)})promise2= |