手寫Promise原理

個人promise能實現什麼?

1:解決回調地獄,實現異步

2:能夠鏈式調用,能夠嵌套調用

3:有等待態到成功態的方法,有等待態到失敗態的方法

4:能夠衍生出周邊的方法,如Promise.resolve(),Promise.reject(),Promise.prototype.then(),Promise.prototype.catch(),Promise.all() // 全部的完成

5. 能夠根據本身的需求調節本身的promise

let PromiseA = require('./PromiseA'); const promiseA = new PromiseA((resolve, reject) => { resolve(new PromiseA((resolve,reject)=>{ setTimeout(()=>{ resolve(100) },1000) })) }) promiseA.then(data=>{ console.log(data) })

下面開始實現promise,首先創造三個常量,等待,成功,失敗html

const PENDING = 'PENDING';   // 等待狀態
const RESOLVED = 'RESOLVED';   // 成功狀態
const REJECTED = 'REJECTED';   // 失敗狀態

而後創造一個promiseA類,裏面有constructor,then方法,catch方法。這裏的catch其實就是失敗的then,即then(null,(err)=>{...})jquery


class PromiseA { constructor(){...} then(){...} 
    catch(err){
        return this.then(null,err)
    }
}

咱們重點關注constructor和then,先來看constructor。這裏promiseA默認的狀態是等待態,成功的值value默認爲undefined,失敗的值reason默認爲undefined。這裏的onResolvedCallbacks和onRejectedCallbacks是一個發佈訂閱的數組,咱們先無論。而後有resolve方法,reject方法。若是傳進來是resolve,則改變value,而且讓狀態改成resolved。resolve裏面可能還要promise,若是是的話,則執行它的then方法。reject方法同理,也是改變狀態和值。數組

還有Promise自帶一個executor執行器,就是傳進來的參數。會當即執行 。 但有可能出錯。因此用try,catch包住。 executor裏有倆個參數,就是resolve和reject。也就是promise傳進來參數的倆個resolve,reject方法(constructor裏的resolve和reject)promise

 constructor(executor) { this.status = PENDING; // 默認等待狀態
        this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; let resolve = (value) => { if(value instanceof PromiseA){ value.then(resolve,reject) return } if (this.status === PENDING) { this.value = value; this.status = RESOLVED; this.onResolvedCallbacks.forEach(fn => fn()); } } let reject = (reason) => { if (this.status === PENDING) { this.reason = reason; this.status = REJECTED; this.onRejectedCallbacks.forEach(fn => fn()); } } try { executor(resolve, reject); } catch (e) { reject(e) } }

而後咱們在看看then方法,then方法傳進來倆個參數onFulfilled,onRejected。這是倆個方法。一個成功的回調函數,一個失敗的回調函數。咱們重點看一下,三個狀態的執行,即status的走向。異步

若是是resolve,即執行成功的回調onFulfilled。若是是reject,即執行失敗的回調onRejected。若是是等待,即執行一個發佈訂閱的模式。發佈訂閱 ( 其實就是,我先將成功的回調函數或者失敗的回調函數各自放入對應的數組,便是上面咱們跳過的倆個數組onResolvedCallbacks和onRejectedCallbacks 。而後,當狀態改變爲resolve或者reject的時候,即遍歷onResolvedCallbacks或者onRejectedCallbacks執行對應的回調函數 。至此異步就實現了,這個異步解決就是靠發佈訂閱模式來解決的。)函數

三個狀態有三種走向,成功,失敗,等待。不管是哪一種狀態,最終都會執行成功的回調函數onFulfilled或者失敗的回調函數onRejected。至此promise的基礎功能就實現了。學習

 then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled:v=>v onRejected = typeof onRejected === 'function' ? onRejected:e=>{throw e} let promise2 = new PromiseA((resolve, reject) => { if (this.status === RESOLVED) { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }); } if (this.status === REJECTED) { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }); } if (this.status === PENDING) { this.onResolvedCallbacks.push(() => { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(err); } }); }) this.onRejectedCallbacks.push(() => { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(err); } }); }) } }) return promise2; }

 

接下來咱們繼續實現鏈式調用,鏈式調用的方法就是建立一個新的promise2對象,而後獲得以前的promise的then方法的值。是怎麼獲得以前的then方法裏面的值呢?就是上面的promise2和resolvePromise方法。咱們接着往下看。ui

let promise2 = promiseA.then(e=>{ return e } ) promise2.then(data=>{ console.log(data,'123') })

那麼繼續實現,仍是上面的函數。這裏咱們重點觀察這個promise2和resolvePromise. 咱們先說promise2,這裏的promise2,實際上是一個新的promise.也就是說promise的鏈式調用靠的就是返回一個新的promise.這裏把以前的三種狀態包起來,目的就是可讓裏面獲得的結果,執行PromiseA的resolve方法和reject方法。有人可能會說,那麼PromiseA的resolve或者reject要仍是promise怎麼辦?這裏咱們就要用到resolvePromise方法來判斷了。因此咱們將成功的回調函數resolve換成resolvePromise方法來執行。這裏咱們要明白,resolve是執行成功的回調函數。無論狀態是成功仍是失敗,若是執行成功都是走向resolve。因此resolve和reject的狀態若是執行成功都是走向resolve。this

let promise2 = new PromiseA((resolve, reject) => { if (this.status === RESOLVED) { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }); } if (this.status === REJECTED) { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }); } if (this.status === PENDING) { this.onResolvedCallbacks.push(() => { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(err); } }); }) this.onRejectedCallbacks.push(() => { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(err); } }); }) } }) return promise2;

而後咱們在看resolvePromise方法,走到這裏,說明是執行成功的回調函數了。傳進去的參數有promise2,x,promise2的resolve,promise2的reject。首先promise2等於一個構造函數PromiseA。 這樣傳進去參數是會報錯的。由於執行到這一步,promise2尚未生成。因此是會報錯的。spa

因此咱們加一個setTimeout包住它,這樣就能夠等promise2生成完在執行。上面代碼的setTimeout涉及到了事件循環,也就是宏任務和微任務的部分。js執行機制是先執行主線程,而後執行微任務隊列,而後進行渲染,而後在執行宏任務隊列。宏任務執行完,若是宏任務裏還包着js任務,就繼續循環反覆。直到全部任務執行完成。這裏的setTimeout是宏任務,promise2執行就是主線程,主線程在宏任務以前執行。

resolvePromise(promise2, x, resolve, reject);

 

剛剛說到setTimeout,這裏貼上一段代碼。這裏的newPromise是在setTimeout前執行的。

console.log(1);

setTimeout(() => {
  console.log("我是定時器,延遲0S執行的");
}, 0);

new Promise((resolve, reject) => {
  console.log("new Promise是同步任務裏面的宏任務");
  resolve("我是then裏面的參數,promise裏面的then方法是宏任務裏面的微任務");
}).then(data => {
  console.log(data);
});

console.log(2);

 

好的參數都傳進去了,接下來咱們看resolvePromise的具體方法。就是一個判斷回調函數x是否是promise,若是是就在循環拆開。直到不是爲止。若是是普通值的話就能夠直接返回了。至此,因此的promise庫就實現完了。至於後面的all和其餘周邊方法就是語法糖了。主要核心部分掌握了,後面的周邊方法就不算什麼了。

function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) { return reject(new TypeError('返回的promise和當前promise不能是同一個對象哦,會嵌入死循環')) } if ((typeof x === 'object' && x !== null) || typeof x === 'function') { try { let then = x.then; if (typeof then === 'function') { then.call(x,y=> { resolvePromise(promise2, y, resolve, reject) },r=> { reject(r) } ) } else { resolve(x) } } catch (err) { reject(err) } } else { resolve(x); } }

 至於constructor裏的resolve方法裏判斷,咱們來看看。其實也是一個遞歸。判斷PromiseA裏的resolve裏面是否是promise,一直拆開。跟上面的方法相似。

let resolve = (value) => { if(value instanceof PromiseA){ value.then(resolve,reject) return } if (this.status === PENDING) { this.value = value; this.status = RESOLVED; this.onResolvedCallbacks.forEach(fn => fn()); } }

而至於這倆行代碼,就是解決一個不斷向jquery那樣then的狀況。若是是函數的話,將本身的參數做爲結果返回。傳遞給下一個then。

onFulfilled = typeof onFulfilled === 'function' ? onFulfilled:v=>v onRejected = typeof onRejected === 'function' ? onRejected:e=>{throw e}
promiseA.then().then().then().then().then()

 

 

最後,若是有什麼以爲奇怪的地方,歡迎互相討論,學習。謝謝!

原文出處:https://www.cnblogs.com/At867604340/p/12486678.html

相關文章
相關標籤/搜索