首先咱們先看一下promise是如何使用的:javascript
<script> let p1=new Promise((resolve,reject)=>{ resolve() }) p1.then(()=>{ console.log(123) }) </script>
經過promise構建出來的對象有三種狀態,Pending(進行中),Fulfilled(已成功),Rejected(已失敗)java
狀態只能由 Pending 變爲 Fulfilled 或由 Pending 變爲 Rejected ,且狀態改變以後不會在發生變化,會一直保持這個狀態。
經過then函數註冊成功或者失敗函數,promise內部調用resolve或者reject調用對應的函數。jquery
; (function () { const status = { 0: "pending", 1: "fulfilled", 2: "rejected" } class customePromise { constructor(func) { if (typeof func != "function") { throw TypeError("Promise resolver " + func + " is not a function") } func(this._resolve.bind(this), this._reject.bind(this)); } _resolve(val) { } _reject(val) { } } window.customePromise = customePromise; })();
首先咱們定義了一個customePromise的類,經過window暴露出去,經過前面promise調用咱們能夠看到參數是一個函數,該函數接受兩個參數,resolve,reject,經過調用兩個函數來執行對應的成功或者失敗函數。第一步,咱們在內部首先要判斷傳入是否爲一個函數,才進行下一步操做。數組
; (function () { const status = { 0: "pending", 1: "fulfilled", 2: "rejected" } class customePromise { constructor(func) { this._status = status[0]; this.resolveArr = []; this.rejectArr = []; this.value=""; if(typeof func!="function"){ throw TypeError("Promise resolver "+ func+" is not a function") } try { func(this._resolve.bind(this), this._reject.bind(this)); } catch (err) { this._reject(err) } } _resolve(val) { } _reject(val) { } } window.customePromise = customePromise; })();
promise最大的好處就是他的捕獲機制,一旦你在外部函數吃錯均可以在內部能夠捕獲到,這樣你外面的腳本還能繼續往下執行,並經過失敗函數能夠打印出來。promise
一開始咱們在構造函數開始執行的時候保存了這個對象狀態爲pending,而後在這個promise對象上掛載了兩個屬性用來保存成功和失敗函數。下面咱們來實現then函數異步
then(resolveFunc, rejectFunc) { switch (this._status) { case "pending": this.resolveArr.push(resolveFunc); this.rejectArr.push(rejectFunc); break; case "fulfilled": resolvefunc(this.value) break; case "rejected": rejectfunc(this.value) break; } }
經過判斷promise對象來決定函數是否執行,若是處於pending狀態,就把成功失敗函數存入對應的數組,若是狀態已經改變,就執行對應的函數。下面實現成功失敗函數的代碼。函數
_resolve(val) { setTimeout(() => { this.value=val; this._status = status[1]; this.resolveArr.forEach(item => { item(val); }) }, 0) } _reject(val) { setTimeout(() => { this.value=val; this._status = status[2]; this.rejectArr.forEach(item => { item(val); }) }, 0) }
注意下咱們的promise屬於微任務,他會讓咱們主線程的任務先執行,因此咱們這裏採用異步模擬。但這裏仍是存在一個問題,就是咱們能夠在外部屢次調用,這明顯是不容許,因此咱們來優化下代碼。優化
_resolve(val) { setTimeout(() => { if (this._status != status[0]) { return; } this.value=val; this._status = status[1]; this.resolveArr.forEach(item => { item(val); }) }, 0) } _reject(val) { setTimeout(() => { if (this._status != status[0]) { return; } this.value=val; this._status = status[2]; this.rejectArr.forEach(item => { item(val); }) }, 0) }
用過jquery的同窗都知道jquery是能夠鏈式調用,一樣咱們的promise也是能夠的,那就表示咱們的then函數返回值一樣也是個promise對象,下面咱們來改寫then函數。this
then(resolveFunc, rejectFunc) { let resolvefunc, rejectfunc; let _this = this; return new customePromise(function (resolve, reject) { resolvefunc = function (val) { let value = resolveFunc(val); resolve(value) } rejectfunc = function (val) { let value = rejectFunc(val); reject(value) } switch (_this._status) { case "pending": _this.resolveArr.push(resolvefunc); _this.rejectArr.push(rejectfunc); break; case "fulfilled": resolvefunc() break; case "rejected": rejectfunc break; } }) }
注意這裏並很差理解,由於以前咱們存入的事成功和失敗函數,這裏咱們包裝了一個函數並把then函數返回值promise對象的resolve函數也存入進去,這就表示若是咱們第一個promise狀態改變會接着觸發第二個promise對象的執行,但須要注意若是咱們函數的返回值一樣是一個promise對象的話,咱們必要要等待其狀態改變才能觸發他的下一個的then函數,這是一個難點,很很差理解,咱們使用過把then函數返回的promise對象的resolve時間掛在函數內部的then上面,一旦內部函數狀態改變會觸發then函數的對應的事件,固然咱們也是要加上容錯處理,下面附上代碼: 線程
then(resolveFunc, rejectFunc) { let resolvefunc, rejectfunc; let _this = this; return new customePromise(function (resolve, reject) { resolvefunc = function (val) { try { if (typeof resolveFunc != "function") { resolve(val) } else { let value = resolveFunc(val); if (value instanceof customePromise) { value.then(resolve) } else { resolve(value) } } } catch (err) { console.log(err) reject(err) } } rejectfunc = function (val) { try { if (typeof rejectFunc != "function") { resolve(val) } else { let value = rejectFunc(val); if (value instanceof customePromise) { value.then(reject) } else { reject(value) } } } catch (err) { reject(err) } } switch (_this._status) { case "pending": _this.resolveArr.push(resolvefunc); _this.rejectArr.push(rejectfunc); break; case "fulfilled": resolvefunc() break; case "rejected": rejectfunc break; } }) }
最後說一下resolve()參數若是是一個promise對象的話,必需要等待參數的狀態改變才能觸發他的then函數,原理跟上面的很類似。
_resolve(val) { setTimeout(() => { if (val instanceof customePromise) { val.then(()=>{ this._status = status[1]; this.resolveArr.forEach(item => { item(val); }) }) return; } if (this._status != status[0]) { return; } this._status = status[1]; this.resolveArr.forEach(item => { item(val); }) }, 0) } _reject(val) { setTimeout(() => { if (val instanceof customePromise) { val.then(()=>{ this._status = status[2]; this.rejectArr.forEach(item => { item(val); }) }) return; } if (this._status != status[0]) { return; } this._status = status[2]; this.rejectArr.forEach(item => { item(val); }) }, 0) }
最後附上全部的代碼:
; (function () { const status = { 0: "pending", 1: "fulfilled", 2: "rejected" } class customePromise { constructor(func) { this._status = status[0]; this.resolveArr = []; this.rejectArr = []; if(typeof func!="function"){ throw TypeError("Promise resolver "+ func+" is not a function") } try { func(this._resolve.bind(this), this._reject.bind(this)); } catch (err) { this._reject(err) } } _resolve(val) { setTimeout(() => { if (val instanceof customePromise) { val.then(()=>{ this._status = status[1]; this.resolveArr.forEach(item => { item(val); }) }) return; } if (this._status != status[0]) { return; } this._status = status[1]; this.resolveArr.forEach(item => { item(val); }) }, 0) } _reject(val) { setTimeout(() => { if (val instanceof customePromise) { val.then(()=>{ this._status = status[2]; this.rejectArr.forEach(item => { item(val); }) }) return; } if (this._status != status[0]) { return; } this._status = status[2]; this.rejectArr.forEach(item => { item(val); }) }, 0) } then(resolveFunc, rejectFunc) { let resolvefunc, rejectfunc; let _this = this; return new customePromise(function (resolve, reject) { resolvefunc = function (val) { try { if (typeof resolveFunc != "function") { resolve(val) } else { let value = resolveFunc(val); if (value instanceof customePromise) { value.then(resolve) } else { resolve(value) } } } catch (err) { console.log(err) reject(err) } } rejectfunc = function (val) { try { if (typeof rejectFunc != "function") { resolve(val) } else { let value = rejectFunc(val); if (value instanceof customePromise) { value.then(reject) } else { reject(value) } } } catch (err) { reject(err) } } switch (_this._status) { case "pending": _this.resolveArr.push(resolvefunc); _this.rejectArr.push(rejectfunc); break; case "fulfilled": resolvefunc() break; case "rejected": rejectfunc break; } }) } } window.customePromise = customePromise; })();