上一篇文章中,我們介紹了Promise的基本使用,在這篇文章中,我們試着自己來寫一個Promise,主要是學習Promise的內部機制,學習它的編程思想。
!!!備注:本文寫的不好,僅供自己學習之用,具體的實現過程建議看下面的參考文章。所以本文沒有發布到博客園首頁和其他地方
Promise API分析
正常使用方法
我們來看一個正常的使用:
var p=new Promise(function(resolve,rejcet){ setTimeout(function(){ if(true){ resolve('success'); }else{ rejcet('failure'); } },1000); }); p.then(function(value){ console.log(value); },function(error){ console.log(error); }); //success
接下來我們就來實現這么一個Promise.
先來了解相關的一些術語:
解決(fulfill):指一個 promise 成功時進行的一系列操作,如狀態的改變、回調的執行。雖然規范中用 fulfill 來表示解決,但在后世的 promise 實現多以 resolve 來指代之。
拒絕(reject):指一個 promise 失敗時進行的一系列操作。
終值(eventual value):所謂終值,指的是 promise 被解決時傳遞給解決回調的值,由於 promise 有一次性的特征,因此當這個值被傳遞時,標志着 promise 等待態的結束,故稱之終值,有時也直接簡稱為值(value)。
據因(reason):也就是拒絕原因,指在 promise 被拒絕時傳遞給拒絕回調的值。
Promise的流程圖分析
promise的執行流程如如下:
Promise鏈式操作中,執行順序是如何保證的
每個promise后面鏈一個對象該對象包含onfulfiled,onrejected,子promise三個屬性,當父promise 狀態改變完畢,執行完相應的onfulfiled/onfulfiled的時候呢,拿到子promise,在等待這個子promise狀態改變,再執行相應的onfulfiled/onfulfiled。依次循環直到當前promise沒有子promise
如何讓異步的value在thenable函數中拿到
將resolve/reject函數和onfulfiled/onrejected放入同一個對象(promise對象)里面,resolve/reject的時候將value設置this.value=xxx。onfulfiled/onrejected執行的時候呢,onfulfiled(this.value)即可。
在這里避免頭暈,解釋一下,onfulfilled和onrejected指的是then里面的兩個函數。
狀態機制切換
如圖所示,狀態只能由pengding-->fulfilled,或者由pending-->rejected這樣轉變。
只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對Promise
對象添加回調函數,也會立即得到這個結果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。
開始手寫Promise
1、 首先我們來寫好我們的框架
ES6原生的構建方式為:
// Promise構造函數接收一個executor函數,executor函數執行完同步或異步操作后,調用它的兩個參數resolve和reject var promise = new Promise(function(resolve, reject) { /* 如果操作成功,調用resolve並傳入value 如果操作失敗,調用reject並傳入reason */ })
我們就按照這種方式來搭好框架
function Promise(callback) { var self = this self.status = 'PENDING' // Promise當前的狀態 self.data = undefined // Promise的值 self.onResolvedCallback = [] // Promise resolve時的回調函數集 self.onRejectedCallback = [] // Promise reject時的回調函數集 callback(resolve, reject) // 執行executor並傳入相應的參數 function resolve(value){ } function rejecte(error){ } } // 添加我們的then方法 Promise.prototype.then=function(){ }
我們構造一個Promise函數,並傳入一個回調callback,callback里面傳入兩個函數作為參數,一個是resove,一個是reject。並在Promise的原型上加入我們的then方法。
2、完善框架里面的內容
框架搭好了,接下來我們來一點點的完善框架里面的內容,可以這么說,把resolve,reject和then補充完,基本可以說就是把Promise完成了。
我們先來完善我們的resolve和rejected:
function Promise(callback) { var self = this self.status = 'PENDING' // Promise當前的狀態 self.data = undefined // Promise的值 self.onResolvedCallback = [] // Promise resolve時的回調函數集 self.onRejectedCallback = [] // Promise reject時的回調函數集 callback(resolve, reject) // 執行executor並傳入相應的參數 function resolve(value){ if(self.status=='PENDING'){ self.status=='FULFILLED'; self.data=value; // 依次執行成功之后的函數棧 for(var i = 0; i < self.onResolvedCallback.length; i++) { self.onResolvedCallback[i](value) } } } function rejecte(error){ if (self.status === 'PENDING') { self.status = 'REJECTED' self.data = error; // 依次執行失敗之后的函數棧 for(var i = 0; i < self.onRejectedCallback.length; i++) { self.onRejectedCallback[i](error) } } } }
如果是penging,則改變相應的狀態,並把resolve和reject的值保存子data里面。
接下來我們實現我們的then方法:
then方法是Promise的核心,因此這里會花比較大的篇幅去介紹then:
一個promise的then接受兩個參數:
promise.then(onFulfilled, onRejected)
onFulfilled
和 onRejected
都是可選參數。
- 如果
onFulfilled
不是函數,其必須被忽略 - 如果
onRejected
不是函數,其必須被忽略
onFulfilled
特性
如果 onFulfilled
是函數:
- 當
promise
執行結束后其必須被調用,其第一個參數為promise
的終值,也就是resolve傳過來的值 - 在
promise
執行結束前其不可被調用 - 其調用次數不可超過一次
onRejected
特性
如果 onRejected
是函數:
- 當
promise
被拒絕執行后其必須被調用,其第一個參數為promise
的據因,也就是reject傳過來的值 - 在
promise
被拒絕執行前其不可被調用 - 其調用次數不可超過一次
調用時機
onFulfilled
和 onRejected
只有在執行環境堆棧僅包含平台代碼時才可被調用(平台代碼指引擎、環境以及 promise 的實施代碼)
調用要求
onFulfilled
和 onRejected
必須被作為函數調用(即沒有 this
值,在 嚴格模式(strict) 中,函數 this
的值為 undefined
;在非嚴格模式中其為全局對象。)
多次調用
then
方法可以被同一個 promise
調用多次
- 當
promise
成功執行時,所有onFulfilled
需按照其注冊順序依次回調 - 當
promise
被拒絕執行時,所有的onRejected
需按照其注冊順序依次回調
返回
then
方法必須返回一個 promise
對象
promise2 = promise1.then(onFulfilled, onRejected);
- 如果
onFulfilled
或者onRejected
返回一個值x
,則運行下面的 Promise 解決過程:[[Resolve]](promise2, x)
- 如果
onFulfilled
或者onRejected
拋出一個異常e
,則promise2
必須拒絕執行,並返回拒因e
- 如果
onFulfilled
不是函數且promise1
成功執行,promise2
必須成功執行並返回相同的值 - 如果
onRejected
不是函數且promise1
拒絕執行,promise2
必須拒絕執行並返回相同的據因
不論 promise1
被 reject 還是被 resolve 時 promise2
都會被 resolve,只有出現異常時才會被 rejected。
每個Promise對象都可以在其上多次調用then方法,而每次調用then返回的Promise的狀態取決於那一次調用then時傳入參數的返回值,所以then不能返回this,因為then每次返回的Promise的結果都有可能不同。
接下來我們來寫我們的then方法:
Promise.prototype.then = function(onResolved, onRejected) { var self = this var promise2 // 根據標准,如果then的參數不是function,則我們需要忽略它,此處以如下方式處理 onResolved = typeof onResolved === 'function' ? onResolved : function(value) {} onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {} if (self.status === 'resolved') { // 如果promise1(此處即為this/self)的狀態已經確定並且是resolved,我們調用onResolved // 因為考慮到有可能throw,所以我們將其包在try/catch塊里 return promise2 = new Promise(function(resolve, reject) { try { var x = onResolved(self.data) if (x instanceof Promise) { // 如果onResolved的返回值是一個Promise對象,直接取它的結果做為promise2的結果 x.then(resolve, reject) } resolve(x) // 否則,以它的返回值做為promise2的結果 } catch (e) { reject(e) // 如果出錯,以捕獲到的錯誤做為promise2的結果 } }) } // 此處與前一個if塊的邏輯幾乎相同,區別在於所調用的是onRejected函數,就不再做過多解釋 if (self.status === 'rejected') { return promise2 = new Promise(function(resolve, reject) { try { var x = onRejected(self.data) if (x instanceof Promise) { x.then(resolve, reject) } } catch (e) { reject(e) } }) } if (self.status === 'pending') { // 如果當前的Promise還處於pending狀態,我們並不能確定調用onResolved還是onRejected, // 只能等到Promise的狀態確定后,才能確實如何處理。 // 所以我們需要把我們的**兩種情況**的處理邏輯做為callback放入promise1(此處即this/self)的回調數組里 // 邏輯本身跟第一個if塊內的幾乎一致,此處不做過多解釋 return promise2 = new Promise(function(resolve, reject) { self.onResolvedCallback.push(function(value) { try { var x = onResolved(self.data) if (x instanceof Promise) { x.then(resolve, reject) } } catch (e) { reject(e) } }) self.onRejectedCallback.push(function(reason) { try { var x = onRejected(self.data) if (x instanceof Promise) { x.then(resolve, reject) } } catch (e) { reject(e) } }) }) } } // 為了下文方便,我們順便實現一個catch方法 Promise.prototype.catch = function(onRejected) { return this.then(null, onRejected) }
完整代碼如下:

try { module.exports = Promise } catch (e) {} function Promise(executor) { var self = this self.status = 'pending' self.onResolvedCallback = [] self.onRejectedCallback = [] function resolve(value) { if (value instanceof Promise) { return value.then(resolve, reject) } setTimeout(function() { // 異步執行所有的回調函數 if (self.status === 'pending') { self.status = 'resolved' self.data = value for (var i = 0; i < self.onResolvedCallback.length; i++) { self.onResolvedCallback[i](value) } } }) } function reject(reason) { setTimeout(function() { // 異步執行所有的回調函數 if (self.status === 'pending') { self.status = 'rejected' self.data = reason for (var i = 0; i < self.onRejectedCallback.length; i++) { self.onRejectedCallback[i](reason) } } }) } try { executor(resolve, reject) } catch (reason) { reject(reason) } } function resolvePromise(promise2, x, resolve, reject) { var then var thenCalledOrThrow = false if (promise2 === x) { return reject(new TypeError('Chaining cycle detected for promise!')) } if (x instanceof Promise) { if (x.status === 'pending') { //because x could resolved by a Promise Object x.then(function(v) { resolvePromise(promise2, v, resolve, reject) }, reject) } else { //but if it is resolved, it will never resolved by a Promise Object but a static value; x.then(resolve, reject) } return } if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) { try { then = x.then //because x.then could be a getter if (typeof then === 'function') { then.call(x, function rs(y) { if (thenCalledOrThrow) return thenCalledOrThrow = true return resolvePromise(promise2, y, resolve, reject) }, function rj(r) { if (thenCalledOrThrow) return thenCalledOrThrow = true return reject(r) }) } else { resolve(x) } } catch (e) { if (thenCalledOrThrow) return thenCalledOrThrow = true return reject(e) } } else { resolve(x) } } Promise.prototype.then = function(onResolved, onRejected) { var self = this var promise2 onResolved = typeof onResolved === 'function' ? onResolved : function(v) { return v } onRejected = typeof onRejected === 'function' ? onRejected : function(r) { throw r } if (self.status === 'resolved') { return promise2 = new Promise(function(resolve, reject) { setTimeout(function() { // 異步執行onResolved try { var x = onResolved(self.data) resolvePromise(promise2, x, resolve, reject) } catch (reason) { reject(reason) } }) }) } if (self.status === 'rejected') { return promise2 = new Promise(function(resolve, reject) { setTimeout(function() { // 異步執行onRejected try { var x = onRejected(self.data) resolvePromise(promise2, x, resolve, reject) } catch (reason) { reject(reason) } }) }) } if (self.status === 'pending') { // 這里之所以沒有異步執行,是因為這些函數必然會被resolve或reject調用,而resolve或reject函數里的內容已是異步執行,構造函數里的定義 return promise2 = new Promise(function(resolve, reject) { self.onResolvedCallback.push(function(value) { try { var x = onResolved(value) resolvePromise(promise2, x, resolve, reject) } catch (r) { reject(r) } }) self.onRejectedCallback.push(function(reason) { try { var x = onRejected(reason) resolvePromise(promise2, x, resolve, reject) } catch (r) { reject(r) } }) }) } } Promise.prototype.catch = function(onRejected) { return this.then(null, onRejected) } Promise.deferred = Promise.defer = function() { var dfd = {} dfd.promise = new Promise(function(resolve, reject) { dfd.resolve = resolve dfd.reject = reject }) return dfd }
本文沒有寫好,有點頭暈,所以具體的實現過程還是建議看下面的參考文章。
參考: