相信你們常常使用Promise
,或者使用Generator
、asnyc/await
等異步解決方案,網上的Promise
原理也遍地開花。
一直以來想抽出時間也寫一寫Promise
實現,可是日常工做也是忙的不可開交,正好元旦放了3天假期,休息了2天半,抽出半天時間來看一看Promise
。javascript
new Promise((resolve, reject) => { setTimeout(() => { resolve(1); }, 1000) }).then((data) => { console.log(data); return new Promise((res) => { setTimeout(() => { res(2); },1000) }) }).then((res) => { console.log(res); }) 複製代碼
Promise
是一個包含了兼容promise
規範then
方法的對象或函數。thenable
是一個包含了then
方法的對象或函數。value
是任何Javascript
值。 (包括 undefined
, thenable
, Promise
等)。exception
是由throw
表達式拋出來的值。reason
是一個用於描述Promise
被拒絕緣由的值。因爲Promise/A+
規範並不包括catch
、race
、all
等方法的實現,因此這裏也不會去詳細解釋。java
一個Promise
必須處在其中之一的狀態:pending
, fulfilled
或 rejected
。 若是是pending
狀態,則promise
:git
能夠轉換到fulfilled
或rejected
狀態。 若是是fulfilled
狀態,則promise
:github
若是是rejected
狀態,則promise
能夠:segmentfault
function MyPromise(callback) { let that = this; //定義初始狀態 //Promise狀態 that.status = 'pending'; //value that.value = 'undefined'; //reason 是一個用於描述Promise被拒絕緣由的值。 that.reason = 'undefined'; //定義resolve function resolve(value) { //當status爲pending時,定義Javascript值,定義其狀態爲fulfilled if(that.status === 'pending') { that.value = value; that.status = 'resolved'; } } //定義reject function reject(reason) { //當status爲pending時,定義reason值,定義其狀態爲rejected if(that.status === 'pending') { that.reason = reason; that.status = 'rejected'; } } //捕獲callback是否報錯 try { callback(resolve, reject); } catch (error) { reject(error); } } 複製代碼
Promise
對象有一個then
方法,用來註冊在這個Promise
狀態肯定後的回調,then
方法接受兩個參數: Promise.then(onFulfilled,onRejected)
。
咱們把then函數寫在原型上。數組
MyPromise.prototype.then = function(onFulfilled, onRejected) { let that = this; if(that.status === 'resolved') { onFulfilled(that.value); } if(that.status === 'rejected') { onRejected(that.reason); } } 複製代碼
上述代碼只是實現了Promise
的最基本邏輯,若是直接調用then
是能夠執行的,可是並不支持異步,而Promise
最大的特色就是解決callback
異步回調地獄的問題。
因此咱們來改造下。promise
function MyPromise(callback) { let that = this; //定義初始狀態 //Promise狀態 that.status = 'pending'; //value that.value = 'undefined'; //reason 是一個用於描述Promise被拒絕緣由的值。 that.reason = 'undefined'; //用來解決異步問題的數組 that.onFullfilledArray = []; that.onRejectedArray = []; //定義resolve function resolve(value) { //當status爲pending時,定義Javascript值,定義其狀態爲fulfilled if(that.status === 'pending') { that.value = value; that.status = 'resolved'; that.onFullfilledArray.forEach((func) => { func(that.value); }); } } //定義reject function reject(reason) { //當status爲pending時,定義reason值,定義其狀態爲rejected if(that.status === 'pending') { that.reason = reason; that.status = 'rejected'; that.onRejectedArray.forEach((func) => { func(that.reason); }); } } //捕獲callback是否報錯 try { callback(resolve, reject); } catch (error) { reject(error); } } 複製代碼
then
函數的改造bash
MyPromise.prototype.then = function(onFulfilled, onRejected) { let that = this; //須要修改下,解決異步問題,即當Promise調用resolve以後再調用then執行onFulfilled(that.value)。 //用兩個數組保存下onFulfilledArray if(that.status === 'pending') { that.onFullfilledArray.push((value) => { onFulfilled(value); }); that.onRejectedArray.push((reason) => { onRejected(reason); }); } if(that.status === 'resolved') { onFulfilled(that.value); } if(that.status === 'rejected') { onRejected(that.reason); } } 複製代碼
因爲Promise/A+
規範規定一個Promise
必須處在其中之一的狀態:pending
, fulfilled
或 rejected
,因此在用戶使用Promise
時,寫的是異步代碼的話,那麼此時Promise
必定是處於pending
狀態,反之正常調用。
所以,初始化Promise
時,定義兩個數組爲onFullfilledArray
,onRejectedArray
,用來保存then
函數的兩個回調函數onFulfilled
和onRejected
。同時咱們在then
函數中判斷status
是不是pending
,而後將onFulfilled
和onRejected
分別傳入對應數組中。當用戶調用resolve
或reject
時,更改狀態,遍歷數組,執行onFulfilled
或者onRejected
,從而異步調用。markdown
在Promise/A+
規範中:異步
對於一個promise,它的then方法能夠調用屢次
promise
fulfilled
後,全部onFulfilled
都必須按照其註冊順序執行。promise
rejected
後,全部OnRejected
都必須按照其註冊順序執行。then 必須返回一個promise
onFulfilled
或 onRejected
返回了值x
, 則執行Promise
解析流程[[Resolve]](promise2, x)
。onFulfilled
或 onRejected
拋出了異常e
, 則promise2
應當以e
爲reason
被拒絕。onFulfilled
不是一個函數且promise1
已經fulfilled
,則promise2
必須以promise1
的值fulfilled
。OnReject
不是一個函數且promise1
已經rejected
, 則promise2
必須以相同的reason
被拒絕。MyPromise.prototype.then = function(onFulfilled, onRejected) { let that = this; let promise2; // 根據標準,若是then的參數不是function,則咱們須要忽略它 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(f) {}; onRejected = typeof onRejected === 'function' ? onRejected : function(r) {}; //須要修改下,解決異步問題,即當Promise調用resolve以後再調用then執行onFulfilled(that.value)。 //用兩個數組保存下onFulfilledArray if(that.status === 'pending') { return promise2 = new Promise(function(resolve, reject) { that.onFullfilledArray.push((value) => { try { let x = onFulfilled(that.value); //判斷onFulfilled是不是一個Promise,若是是,那麼就直接把MyPromise中的resolve和reject傳給then; //返回值是一個Promise對象,直接取它的結果作爲promise2的結果 if(x instanceof MyPromise) { x.then(resolve, reject); } //不然,以它的返回值作爲promise2的結果 resolve(x); } catch (error) { reject(error); } }); that.onRejectedArray.push((value) => { try { let x = onRejected(that.value); //判斷onRejected是不是一個Promise,若是是,那麼就直接把MyPromise中的resolve和reject傳給then; //返回值是一個Promise對象,直接取它的結果作爲promise2的結果 if(x instanceof MyPromise) { x.then(resolve, reject); } //不然,以它的返回值作爲promise2的結果 resolve(x); } catch (error) { reject(error); } }); }) } if(that.status === 'fulfilled') { return promise2 = new MyPromise(function(resolve, reject) { try { let x = onFulfilled(that.value); //判斷onFulfilled是不是一個Promise,若是是,那麼就直接把MyPromise中的resolve和reject傳給then; //返回值是一個Promise對象,直接取它的結果作爲promise2的結果 if(x instanceof MyPromise) { x.then(resolve, reject); } //不然,以它的返回值作爲promise2的結果 resolve(x); } catch (error) { reject(error); } }) } if(that.status === 'rejected') { return new MyPromise(function(resolve, reject) { try { let x = onRejected(that.value); //判斷onRejected是不是一個Promise,若是是,那麼就直接把MyPromise中的resolve和reject傳給then; //返回值是一個Promise對象,直接取它的結果作爲promise2的結果 if(x instanceof MyPromise) { x.then(resolve, reject); } //不然,以它的返回值作爲promise2的結果 resolve(x); } catch (error) { reject(error); } }) } } 複製代碼
在調用then
時,判斷onFulfilled
和onRejected
是不是一個函數,若是不是,返回一個匿名函數,同時必須返回各參數的值,用來解決鏈式調用時Promise
值的穿透問題。
例如:
new MyPromise(resolve=>resolve(8)) .then() .then() .then(function foo(value) { alert(value) }) 複製代碼
因此咱們把這塊改爲這樣:
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(f) { return f}; onRejected = typeof onRejected === 'function' ? onRejected : function(r) {throw r}; 複製代碼
可是這樣每次判斷都須要從新寫這個x
和MyPromise
的關係,因此,咱們須要將這塊代碼給抽象出來,這塊代碼在Promise/A+
規範中叫作resolvePromise
。
function resolvePromise(promise, x, resolve, reject) { let then,thenCalledOrThrow = false //若是promise 和 x 指向相同的值, 使用 TypeError作爲緣由將promise拒絕。 if (promise === x) { return reject(new TypeError('Chaining cycle detected for promise!')) } //判斷x是不是一個Promise,若是是,那麼就直接把MyPromise中的resolve和reject傳給then; //返回值是一個Promise對象,直接取它的結果作爲promise2的結果 if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) { try { then = x.then if (typeof then === 'function') { // typeof //x.then(resolve, reject); then.call(x, function rs(y) { if (thenCalledOrThrow) return thenCalledOrThrow = true return resolvePromise(promise, y, resolve, reject) }, function rj(r) { if (thenCalledOrThrow) return thenCalledOrThrow = true return reject(r) }) } else { return resolve(x) } } catch(e) { if (thenCalledOrThrow) return thenCalledOrThrow = true return reject(e) } } else { return resolve(x) } } 複製代碼
then
函數最後修改成:
MyPromise.prototype.then = function(onFulfilled, onRejected) { let that = this; let promise2; // 根據標準,若是then的參數不是function,則咱們須要忽略它 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(f) { return f}; onRejected = typeof onRejected === 'function' ? onRejected : function(r) {throw r}; //須要修改下,解決異步問題,即當Promise調用resolve以後再調用then執行onFulfilled(that.value)。 //用兩個數組保存下onFulfilledArray if(that.status === 'pending') { return promise2 = new Promise(function(resolve, reject) { that.onFullfilledArray.push((value) => { try { let x = onFulfilled(value); resolvePromise(promise2, x, resolve, reject) } catch(e) { return reject(e) } }); that.onRejectedArray.push((value) => { try { let x = onRejected(value); resolvePromise(promise2, x, resolve, reject) } catch(e) { return reject(e) } }); }) } if(that.status === 'fulfilled') { return promise2 = new MyPromise(function(resolve, reject) { try { let x = onFulfilled(that.value); //處理then的多種狀況 resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error); } }) } if(that.status === 'rejected') { return new MyPromise(function(resolve, reject) { try { let x = onRejected(that.value); //處理then的多種狀況 resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error) } }) } } 複製代碼
測試一下:
new MyPromise((resolve, reject) => { setTimeout(() => { resolve(1); }, 1000) }).then((data) => { console.log(data); return new MyPromise((res) => { setTimeout(() => { res(2); },1000) }) }).then((res) => { console.log(res); }) //1 //2 複製代碼
Promise GITHUB地址。
參考資料:
《Promise/A+規範》
《Promise3》
《實現一個完美符合Promise/A+規範的Promise》
《剖析Promise內部結構,一步一步實現一個完整的、能經過全部Test case的Promise類》