寫一個 符合 promise A+ 規範的 建構函式 MyPromise
阿新 • • 發佈:2018-11-25
promise 是js 非同步發展至今的一個時代的產物 js的非同步 經過如下歷程, 事件監聽 回撥函式 訂閱釋出
promise 是一個時下非同步 的一個解決方案 其核心設計思想主要概括為以下幾點
1:所涉及到的設計模式 : 訂閱釋出模式(觀察者模式 ) 狀態模式
2 :事件迴圈
promise 中的 三個狀態分別為
- pending //進行中
- fulfilled //成功
- rejected //失敗
狀態只能從pending 轉為fulfilled 或者rejected 此為promise 守護的三個狀態 (狀態模式)
因為涉及到非同步呼叫 其實promise還是用的訂閱釋出的原理 來解決非同步這一問題
即 一個成功時的任務佇列 失敗時的任務佇列
私有方法 resolve 和reject 充當的角色為 改變狀態 和 任務訂閱
原型方法 then 方法負責根據當前狀態來進行相應的邏輯處理
promise A+ 規範中 非同步可以基於setTimeout 和process 區別就是在事件迴圈中所處的位置 setTimeout 在處於巨集任務 process 處
於微任務中 注:( ES6中的promise 處於微任務中 此處我們用setTimeout 方式實現 )
具體程式碼如下 :
const PENDING = "pending"; //進行中 const FULFILLED = "fulfilled"; //成功 const REJECTED = "rejected"; //失敗 function MyPromise(executor){ let self = this; //快取當前promise 的例項 self.status = PENDING; //成功回撥任務佇列 self.onResolvedCallbacks = []; //失敗回撥的任務佇列。 self.onRejectedCallbacks = []; //當呼叫此方法的時候如果promise 狀態為pending 可以轉成成功態 // 如果已經是成功態或者失敗態則什麼都不做 function resolve(value){ if (value instanceof Promise) { return value.then(resolve, reject) } setTimeout(function(){ // 非同步執行所有的回撥函式 if(self.status==PENDING){ self.status=FULFILLED; self.value = value; //成功後會有一個不可變的值 self.onResolvedCallbacks.forEach(cb=>cb(self.value)); } }) } function reject(reason){ setTimeout(function(){ // 非同步執行所有的回撥函式 if(self.status==PENDING){ self.status=REJECTED; self.value = reason; self.onRejectedCallbacks.forEach(cb=>cb(self.value)); } }) } try{ //此函式可能會異常 所以需要捕獲。 如果出錯了 需要用reject掉當前promise executor(resolve,reject); }catch(err){ reject(err) } } MyPromise.prototype.then = function(onFulfilled,onRejected){ //前兩句判斷用來做 值的穿透。 onFulfilled = typeof onFulfilled=="function"?onFulfilled:value=>value; onRejected = typeof onRejected=="function"?onRejected:reason=>{throw reason} let self = this; let promise2; //如果當前的promise 狀態已經是陳成功態了。onFulfilled 直接值 if(self.status===FULFILLED){ return promise2 = new MyPromise(function(resolve,reject){ setTimeout(function(){ // 非同步執行所有onFulfilled try{ let x = onFulfilled(self.value); resolvePromise(promise2,x,resolve,reject) }catch(err){ reject(err); } }) }) } if(self.status === REJECTED){ return promise2 = new MyPromise(function(resolve,reject){ setTimeout(function(){ // 非同步執行所有onRejected try{ let x = onRejected(self.value); resolvePromise(promise2,x,resolve,reject) }catch(err){ reject(err); } }) }) } if(self.status===PENDING){ promise2 = new MyPromise(function(resolve,reject){ self.onResolvedCallbacks.push(function(){ let x = onFulfilled(self.value); resolvePromise(promise2,x,resolve,reject) }) self.onRejectedCallbacks.push(function(){ let x = onRejected(self.value); resolvePromise(promise2,x,resolve,reject) }) }) } } function resolvePromise(promise2, x, resolve, reject) { var then var thenCalledOrThrow = false if (promise2 === x) { // 對應標準2.3.1節 return reject(new TypeError('Chaining cycle detected for promise!')) } if (x instanceof Promise) { // 對應標準2.3.2節 // 如果x的狀態還沒有確定,那麼它是有可能被一個thenable決定最終狀態和值的 // 所以這裡需要做一下處理,而不能一概的以為它會被一個“正常”的值resolve if (x.status === PENDING) { x.then(function(value) { resolvePromise(promise2, value, resolve, reject) }, reject) } else { // 但如果這個Promise的狀態已經確定了,那麼它肯定有一個“正常”的值,而不是一個thenable,所以這裡直接取它的狀態 x.then(resolve, reject) } return } if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) { // 2.3.3 try { // 2.3.3.1 因為x.then有可能是一個getter,這種情況下多次讀取就有可能產生副作用 // 即要判斷它的型別,又要呼叫它,這就是兩次讀取 then = x.then if (typeof then === 'function') { // 2.3.3.3 then.call(x, function rs(y) { // 2.3.3.3.1 if (thenCalledOrThrow) return // 2.3.3.3.3 即這三處誰選執行就以誰的結果為準 thenCalledOrThrow = true return resolvePromise(promise2, y, resolve, reject) // 2.3.3.3.1 }, function rj(r) { // 2.3.3.3.2 if (thenCalledOrThrow) return // 2.3.3.3.3 即這三處誰選執行就以誰的結果為準 thenCalledOrThrow = true return reject(r) }) } else { // 2.3.3.4 resolve(x) } } catch (e) { // 2.3.3.2 if (thenCalledOrThrow) return // 2.3.3.3.3 即這三處誰選執行就以誰的結果為準 thenCalledOrThrow = true return reject(e) } } else { // 2.3.4 resolve(x) } } MyPromise.deferred = MyPromise.defer = function() { var dfd = {} dfd.promise = new MyPromise(function(resolve, reject) { dfd.resolve = resolve dfd.reject = reject }) return dfd } module.exports = MyPromise;