1. 程式人生 > >Promise實現原理(附原始碼)

Promise實現原理(附原始碼)

本篇文章主要在於探究 Promise 的實現原理,帶領大家一步一步實現一個 Promise , 不對其用法做說明,如果讀者還對Promise的用法不瞭解,可以檢視阮一峰老師的ES6 Promise教程

接下來,帶你一步一步實現一個 Promise

1. Promise 基本結構

123456 newPromise((resolve,reject)=>{setTimeout(()=>{resolve('FULFILLED')},1000)})

建構函式Promise必須接受一個函式作為引數,我們稱該函式為handlehandle又包含resolvereject兩個引數,它們是兩個函式。

定義一個判斷一個變數是否為函式的方法,後面會用到

123 // 判斷變數否為functionconstisFunction=variable=>typeof variable==='function'

首先,我們定義一個名為 MyPromiseClass,它接受一個函式 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函式包含 resolvereject 兩個引數,它們是兩個函式,可以用於改變 Promise 的狀態和傳入 Promise 的值

123456 newPromise((resolve,reject)=>{setTimeout(()=>{resolve('FULFILLED')},1000)})

這裡 resolve 傳入的 "FULFILLED" 就是 Promise 的值

resolvereject

  • resolve : 將Promise物件的狀態從 Pending(進行中) 變為 Fulfilled(已成功)
  • reject : 將Promise物件的狀態從 Pending(進行中) 變為 Rejected(已失敗)
  • resolvereject 都可以傳入任意型別的值作為實參,表示 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. Promisethen 方法

Promise 物件的 then 方法接受兩個引數:

12 promise.then(onFulfilled,onRejected)

引數可選

onFulfilledonRejected 都是可選引數。

  • 如果 onFulfilledonRejected 不是函式,其必須被忽略

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 函式的引數.
  • xPromise ,這時後一個回撥函式,就會等待該 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=