Promise你們必定都不陌生了,JavaScript異步流程從最初的Callback,到Promise,到Generator,再到目前使用最多的Async/Await(若是對於這些不熟悉的能夠參考我另外一篇文章《JavaScript異步編程》),這不只僅是技術實現的發展,更是思想上對於如何控制異步的遞進。Promise做爲後續方案的基礎,是重中之重,也是面試時候最常被問到的。git
今天咱們就一塊兒從0到1實現一個基於A+規範的Promise,過程當中也會對Promise的異常處理,以及是否可手動終止作一些討論,最後會對咱們實現的Promise作單元測試。完整的代碼已經上傳到github,想直接看代碼的能夠點這裏。github
雖然已經有不少帶你實現Promise類的文章了,但每一個人理解的程度不同,也許不一樣的文章能夠帶給你不一樣的思考呢,那咱們就開始吧。面試
new Promise()時接收一個executor函數做爲參數,該函數會當即執行,函數中有兩個參數,它們也是函數,分別是resolve和reject,函數同步執行必定要放在try...catch中,不然沒法進行錯誤捕獲。算法
MyPromise.js編程
function MyPromise(executor) { function resolve(value) { } function reject(reason) { } try { executor(resolve, reject); } catch (reason) { reject(reason); } } module.exports = MyPromise;
resolve()接收Promise成功值value,reject接收Promise失敗緣由reason。segmentfault
test.js數組
let MyPromise = require('./MyPromise.js'); let promise = new MyPromise(function(resolve, reject) { resolve(123); })
目前實現存在的問題:promise
pending
,成功狀態爲 fulfilled
,失敗狀態爲 rejected
。只能從 pending
-> fulfilled
,或者從 pending
-> rejected
,而且狀態一旦轉變,就永遠不會再變了。因此,咱們須要爲Promise添加一個狀態流轉的機制。微信
MyPromise.js框架
const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; function MyPromise(executor) { let self = this; self.state = PENDING; function resolve(value) { if (self.state === PENDING) { self.state = FULFILLED; } } function reject(reason) { if (self.state === PENDING) { self.state = REJECTED; } } try { executor(resolve, reject); } catch (reason) { reject(reason); } } module.exports = MyPromise;
test.js
let MyPromise = require('./MyPromise.js'); let promise = new MyPromise(function(resolve, reject) { resolve(123); }); promise.then(function(value) { console.log('value', value); }, function(reason) { console.log('reason', reason); })
then
方法Promise擁有一個then
方法,接收兩個函數 onFulfilled
和 onRejected
,分別做爲Promise成功和失敗的回調。因此,在then
方法中咱們須要對狀態state
進行判斷,若是是fulfilled
,則執行onFulfilled(value)
方法,若是是rejected
,則執行onRejected(reason)
方法。
因爲成功值value
和失敗緣由reason
是由用戶在executor
中經過resolve(value)
和 reject(reason)
傳入的,因此咱們須要有一個全局的value
和reason
供後續方法獲取。
MyPromise.js
const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; function MyPromise(executor) { let self = this; self.state = PENDING; self.value = null; self.reason = null; function resolve(value) { if (self.state === PENDING) { self.state = FULFILLED; self.value = value; } } function reject(reason) { if (self.state === PENDING) { self.state = REJECTED; self.reason = reason; } } try { executor(resolve, reject); } catch (reason) { reject(reason); } } MyPromise.prototype.then = function(onFuifilled, onRejected) { let self = this; if (self.state === FULFILLED) { onFuifilled(self.value); } if (self.state === REJECTED) { onRejected(self.reason); } }; module.exports = MyPromise;
目前實現存在的問題:
resolve()
沒有問題,但若是是異步調用,好比放到setTimeout
中,由於目前的代碼在調用then()
方法時,state
還是pending
狀態,當timer到時候調用resolve()
把state
修改成fulfilled
狀態,可是onFulfilled()
函數已經沒有時機調用了。針對上述問題,進行以下修改:
MyPromise.js
const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; function MyPromise(executor) { let self = this; self.state = PENDING; self.value = null; self.reason = null; self.onFulfilledCallbacks = []; self.onRejectedCallbacks = []; function resolve(value) { if (self.state === PENDING) { self.state = FULFILLED; self.value = value; self.onFulfilledCallbacks.forEach(function(fulfilledCallback) { fulfilledCallback(); }); } } function reject(reason) { if (self.state === PENDING) { self.state = REJECTED; self.reason = reason; self.onRejectedCallbacks.forEach(function(rejectedCallback) { rejectedCallback(); }); } } try { executor(resolve, reject); } catch (reason) { reject(reason); } } MyPromise.prototype.then = function(onFuifilled, onRejected) { let self = this; if (self.state === PENDING) { self.onFulfilledCallbacks.push(() => { onFuifilled(self.value); }); self.onRejectedCallbacks.push(() => { onRejected(self.reason); }); } if (self.state === FULFILLED) { onFuifilled(self.value); } if (self.state === REJECTED) { onRejected(self.reason); } }; module.exports = MyPromise;
咱們添加了兩個回調函數數組onFulfilledCallbacks
和onRejectedCallbacks
,用來存儲then()
方法中傳入的成功和失敗回調。而後,當用戶調用resolve()
或reject()
的時候,修改state
狀態,並從相應的回調數組中依次取出回調函數執行。
同時,經過這種方式咱們也實現了能夠註冊多個then()
函數,而且在成功或者失敗時按照註冊順序依次執行。
test.js
let MyPromise = require('./MyPromise.js'); let promise = new MyPromise(function(resolve, reject) { setTimeout(function() { resolve(123); }, 1000); }); promise.then(function(value) { console.log('value1', value); }, function(reason) { console.log('reason1', reason); }); promise.then(function(value) { console.log('value2', value); }, function(reason) { console.log('reason2', reason); });
讀過PromiseA+規範的同窗確定知道,then()
方法返回的還是一個Promise,而且返回Promise的resolve
的值是上一個Promise的onFulfilled()
函數或onRejected()
函數的返回值。若是在上一個Promise的then()
方法回調函數的執行過程當中發生了錯誤,那麼會將其捕獲到,並做爲返回的Promise的onRejected
函數的參數傳入。好比:
let promise = new Promise((resolve, reject) => { resolve(123); }); promise.then((value) => { console.log('value1', value); return 456; }).then((value) => { console.log('value2', value); }); let promise = new Promise((resolve, reject) => { resolve(123); });
打印結果爲:
value1 123
value2 456
let promise = new Promise((resolve, reject) => { resolve(123); }); promise.then((value) => { console.log('value1', value); a.b = 2; // 這裏存在語法錯誤 return 456; }).then((value) => { console.log('value2', value); }, (reason) => { console.log('reason2', reason); });
打印結果爲:
value1 123
reason2 ReferenceError: a is not defined
能夠看到,then()
方法回調函數若是發生錯誤,會被捕獲到,那麼then()
返回的Promise會自動變爲onRejected
,執行onRejected()
回調函數。
let promise = new Promise((resolve, reject) => { reject(123); }); promise.then((value) => { console.log('value1', value); return 456; }, (reason) => { console.log('reason1', reason); return 456; }).then((value) => { console.log('value2', value); }, (reason) => { console.log('reason2', reason); });
打印結果爲:
reason1 123
value2 456
好啦,接下來咱們就去實現then()
方法依然返回一個Promise。
MyPromise.js
MyPromise.prototype.then = function(onFuifilled, onRejected) { let self = this; let promise2 = null; promise2 = new MyPromise((resolve, reject) => { if (self.state === PENDING) { self.onFulfilledCallbacks.push(() => { try { let x = onFuifilled(self.value); self.resolvePromise(promise2, x, resolve, reject); } catch(reason) { reject(reason); } }); self.onRejectedCallbacks.push(() => { try { let x = onRejected(self.reason); self.resolvePromise(promise2, x, resolve, reject); } catch(reason) { reject(reason); } }); } if (self.state === FULFILLED) { try { let x = onFuifilled(self.value); self.resolvePromise(promise2, x, resolve, reject); } catch (reason) { reject(reason); } } if (self.state === REJECTED) { try { let x = onRejected(self.reason); self.resolvePromise(promise2, x, resolve, reject); } catch (reason) { reject(reason); } } }); return promise2; };
能夠看到,咱們新增了一個promise2
做爲then()
方法的返回值。經過let x = onFuifilled(self.value)
或者 let x = onRejected(self.reason)
拿到then()
方法回調函數的返回值,而後調用self.resolvePromise(promise2, x, resolve, reject)
,將新增的promise2
、x
、promise2
的resolve
和reject
傳入到resolvePromise()
中。
因此,下面咱們重點看一下resolvePromise()
方法。
MyPromise.js
MyPromise.prototype.resolvePromise = function(promise2, x, resolve, reject) { let self = this; let called = false; // called 防止屢次調用 if (promise2 === x) { return reject(new TypeError('循環引用')); } if (x !== null && (Object.prototype.toString.call(x) === '[object Object]' || Object.prototype.toString.call(x) === '[object Function]')) { // x是對象或者函數 try { let then = x.then; if (typeof then === 'function') { then.call(x, (y) => { // 別人的Promise的then方法可能設置了getter等,使用called防止屢次調用then方法 if (called) return ; called = true; // 成功值y有可能仍是promise或者是具備then方法等,再次resolvePromise,直到成功值爲基本類型或者非thenable self.resolvePromise(promise2, y, resolve, reject); }, (reason) => { if (called) return ; called = true; reject(reason); }); } else { if (called) return ; called = true; resolve(x); } } catch (reason) { if (called) return ; called = true; reject(reason); } } else { // x是普通值,直接resolve resolve(x); } };
resolvePromise()
是用來解析then()
回調函數中返回的還是一個Promise
,這個Promise
有多是咱們本身的,有多是別的庫實現的,也有多是一個具備then()
方法的對象,因此這裏靠resolvePromise()
來實現統一處理。
下面是翻譯自PromiseA+規範關於resolvePromise()
的要求:
Promise 解決過程
Promise 解決過程是一個抽象的操做,其需輸入一個 promise 和一個值,咱們表示爲 [[Resolve]](promise, x),若是 x 有 then 方法且看上去像一個 Promise ,解決程序即嘗試使 promise 接受 x 的狀態;不然其用 x 的值來執行 promise 。
這種 thenable 的特性使得 Promise 的實現更具備通用性:只要其暴露出一個遵循 Promise/A+ 協議的 then 方法便可;這同時也使遵循 Promise/A+ 規範的實現能夠與那些不太規範但可用的實現能良好共存。
運行 [[Resolve]](promise, x) 需遵循如下步驟:
若是 promise 和 x 指向同一對象,以 TypeError 爲據因拒絕執行 promise
若是 x 爲 Promise ,則使 promise 接受 x 的狀態:
- 若是 x 處於等待態, promise 需保持爲等待態直至 x 被執行或拒絕 - 若是 x 處於執行態,用相同的值執行 promise - 若是 x 處於拒絕態,用相同的據因拒絕 promise
若是 x 爲對象或者函數:
- 把 x.then 賦值給 then - 若是取 x.then 的值時拋出錯誤 e ,則以 e 爲據因拒絕 promise - 若是 then 是函數,將 x 做爲函數的做用域 this 調用之。傳遞兩個回調函數做爲參數,第一個參數叫作 resolvePromise ,第二個參數叫作 rejectPromise: - 若是 resolvePromise 以值 y 爲參數被調用,則運行 [[Resolve]](promise, y) - 若是 rejectPromise 以據因 r 爲參數被調用,則以據因 r 拒絕 promise - 若是 resolvePromise 和 rejectPromise 均被調用,或者被同一參數調用了屢次,則優先採用首次調用並忽略剩下的調用 - 若是調用 then 方法拋出了異常 e: - 若是 resolvePromise 或 rejectPromise 已經被調用,則忽略之 - 不然以 e 爲據因拒絕 promise - 若是 then 不是函數,以 x 爲參數執行 promise - 若是 x 不爲對象或者函數,以 x 爲參數執行 promise
若是一個 promise 被一個循環的 thenable 鏈中的對象解決,而 [[Resolve]](promise, thenable) 的遞歸性質又使得其被再次調用,根據上述的算法將會陷入無限遞歸之中。算法雖不強制要求,但也鼓勵施者檢測這樣的遞歸是否存在,若檢測到存在則以一個可識別的 TypeError 爲據因來拒絕 promise。
參考上述規範,結合代碼中的註釋,相信你們能夠理解resolvePromise()
的做用了。
測試:
test.js
let MyPromise = require('./MyPromise.js'); let promise = new MyPromise(function(resolve, reject) { setTimeout(function() { resolve(123); }, 1000); }); promise.then((value) => { console.log('value1', value); return new MyPromise((resolve, reject) => { resolve(456); }).then((value) => { return new MyPromise((resolve, reject) => { resolve(789); }) }); }, (reason) => { console.log('reason1', reason); }).then((value) => { console.log('value2', value); }, (reason) => { console.log('reason2', reason); });
打印結果:
value1 123
value2 789
then()
方法的回調函數老是異步調用官方Promise
實現的回調函數老是異步調用的:
console.log('start'); let promise = new Promise((resolve, reject) => { console.log('step-'); resolve(123); }); promise.then((value) => { console.log('step--'); console.log('value', value); }); console.log('end');
打印結果:
start
step-
end
step--
value1 123
Promise屬於微任務,這裏咱們爲了方便用宏任務setTiemout
來代替實現異步,具體關於宏任務、微任務以及Event Loop能夠參考個人另外一篇文章帶你完全弄懂Event Loop。
MyPromise.js
MyPromise.prototype.then = function(onFuifilled, onRejected) { let self = this; let promise2 = null; promise2 = new MyPromise((resolve, reject) => { if (self.state === PENDING) { self.onFulfilledCallbacks.push(() => { setTimeout(() => { try { let x = onFuifilled(self.value); self.resolvePromise(promise2, x, resolve, reject); } catch (reason) { reject(reason); } }, 0); }); self.onRejectedCallbacks.push(() => { setTimeout(() => { try { let x = onRejected(self.reason); self.resolvePromise(promise2, x, resolve, reject); } catch (reason) { reject(reason); } }, 0); }); } if (self.state === FULFILLED) { setTimeout(() => { try { let x = onFuifilled(self.value); self.resolvePromise(promise2, x, resolve, reject); } catch (reason) { reject(reason); } }, 0); } if (self.state === REJECTED) { setTimeout(() => { try { let x = onRejected(self.reason); self.resolvePromise(promise2, x, resolve, reject); } catch (reason) { reject(reason); } }, 0); } }); return promise2; };
測試:
test.js
let MyPromise = require('./MyPromise.js'); console.log('start'); let promise = new MyPromise((resolve, reject) => { console.log('step-'); setTimeout(() => { resolve(123); }, 1000); }); promise.then((value) => { console.log('step--'); console.log('value', value); }); console.log('end');
打印結果:
start
step-
end
step--
value1 123
通過以上步驟,一個最基本的Promise就已經實現完了,下面咱們會實現一些不在PromiseA+規範的擴展方法。
catch()
方法then()
方法的onFulfilled
和onRejected
回調函數都不是必傳項,若是不傳,那麼咱們就沒法接收reject(reason)
中的錯誤,這時咱們能夠經過鏈式調用catch()
方法用來接收錯誤。舉例:
let promise = new Promise((resolve, reject) => { reject('has error'); }); promise.then((value) => { console.log('value', value); }).catch((reason) => { console.log('reason', reason); });
打印結果:
reason has error
不只如此,catch()
能夠做爲Promise鏈式調用的最後一步,前面Promise發生的錯誤會冒泡到最後一個catch()
中,從而捕獲異常。舉例:
let promise = new Promise((resolve, reject) => { resolve(123); }); promise.then((value) => { console.log('value', value); return new Promise((resolve, reject) => { reject('has error1'); }); }).then((value) => { console.log('value', value); return new Promise((resolve, reject) => { reject('has error2'); }); }).catch((reason) => { console.log('reason', reason); });
打印結果:
value 123
reason has error1
那麼catch()
方法究竟是如何實現的呢?
答案就是在Promise的實現中,onFulfilled
和onRejected
函數是有默認值的:
MyPromise.js
MyPromise.prototype.then = function(onFuifilled, onRejected) { onFuifilled = typeof onFuifilled === 'function' ? onFuifilled : value => {return value;}; onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}; }; MyPromise.prototype.catch = function(onRejected) { return this.then(null, onRejected); };
能夠看到,onRejected
的默認值是把錯誤reason
經過throw
拋出去。因爲咱們對於同步代碼的執行都是在try...catch
中的,因此若是Promise發生了錯誤,若是沒傳onRejected
,默認的函數會把錯誤reason
拋出,而後會被promise2捕捉到,做爲reject(reason)
決議。
catch()
實現就是調用this.then(null, onRejected)
,因爲promise2
被reject
,因此會執行onRejected
回調,因而就捕捉到了第一個promise的錯誤。
總結來講,then()
方法中不傳onRejected
回調,Promise
內部會默認幫你寫一個函數做爲回調,做用就是throw
拋出reject
或者try...catch
到的錯誤,而後錯誤reason
會被promise2
做爲reject(reason)
進行決議,因而會被下一個then()
方法的onRejected
回調函數調用,而catch
只是寫了一個特殊的then(null, onRejected)
而已。
因此,咱們在寫Promise
的鏈式調用的時候,在then()
中能夠不傳onRejected
回調,只須要在鏈式調用的最末尾加一個catch()
就能夠了,這樣在該鏈條中的Promise
發生的錯誤都會被最後的catch
捕獲到。
舉例1:
let promise = new Promise((resolve, reject) => { reject(123); }); promise.then((value) => { // 注意,不會走這裏,由於第一個promise是被reject的 console.log('value1', value); return new Promise((resolve, reject) => { reject('has error1'); }); }).then((value) => { console.log('value2', value); return new Promise((resolve, reject) => { reject('has error2'); }); }, (reason) => { // 注意,這個then有onRejected回調 console.log('reason2', reason); }).catch((reason) => { // 錯誤在上一個then就被捕獲了,因此不會走到這裏 console.log('reason3', reason); });
打印結果:
reason2 123
舉例2:
let promise = new Promise((resolve, reject) => { reject(123); }); promise.then((value) => { console.log('value1', value); return new Promise((resolve, reject) => { reject('has error1'); }); }).then((value) => { console.log('value2', value); return new Promise((resolve, reject) => { reject('has error2'); }); }).catch((reason) => { // 因爲鏈條中的then都沒有onRejected回調,因此會一直被冒泡到最後的catch這裏 console.log('reason3', reason); });
catch
和then
同樣都是返回一個新的Promise
。有的同窗可能會有疑問,若是catch
中的回調執行也發生錯誤該怎麼辦呢,這個咱們後續在Promise異常處理中再作討論。
打印結果:
reason3 123
finally
方法finally
是某些庫對Promise
實現的一個擴展方法,不管是resolve
仍是reject
,都會走finally
方法。
MyPromise.js
MyPromise.prototype.finally = function(fn) { return this.then(value => { fn(); return value; }, reason => { fn(); throw reason; }); };
done
方法done
方法做爲Promise
鏈式調用的最後一步,用來向全局拋出沒有被Promise
內部捕獲的錯誤,而且再也不返回一個Promise
。通常用來結束一個Promise
鏈。
MyPromise.js
MyPromise.prototype.done = function() { this.catch(reason => { console.log('done', reason); throw reason; }); };
Promise.all
方法Promise.all()
接收一個包含多個Promise
的數組,當全部Promise
均爲fulfilled
狀態時,返回一個結果數組,數組中結果的順序和傳入的Promise
順序一一對應。若是有一個Promise
爲rejected
狀態,則整個Promise.all
爲rejected
。
MyPromise.js
MyPromise.all = function(promiseArr) { return new MyPromise((resolve, reject) => { let result = []; promiseArr.forEach((promise, index) => { promise.then((value) => { result[index] = value; if (result.length === promiseArr.length) { resolve(result); } }, reject); }); }); };
test.js
let MyPromise = require('./MyPromise.js'); let promise1 = new MyPromise((resolve, reject) => { console.log('aaaa'); setTimeout(() => { resolve(1111); console.log(1111); }, 1000); }); let promise2 = new MyPromise((resolve, reject) => { console.log('bbbb'); setTimeout(() => { reject(2222); console.log(2222); }, 2000); }); let promise3 = new MyPromise((resolve, reject) => { console.log('cccc'); setTimeout(() => { resolve(3333); console.log(3333); }, 3000); }); Promise.all([promise1, promise2, promise3]).then((value) => { console.log('all value', value); }, (reason) => { console.log('all reason', reason); })
打印結果:
aaaa
bbbb
cccc
1111
2222
all reason 2222
3333
Promise.race
方法Promise.race()
接收一個包含多個Promise
的數組,當有一個Promise
爲fulfilled
狀態時,整個大的Promise
爲onfulfilled
,並執行onFulfilled
回調函數。若是有一個Promise
爲rejected
狀態,則整個Promise.race
爲rejected
。
MyPromise.js
MyPromise.race = function(promiseArr) { return new MyPromise((resolve, reject) => { promiseArr.forEach(promise => { promise.then((value) => { resolve(value); }, reject); }); }); };
test.js
let MyPromise = require('./MyPromise.js'); let promise1 = new MyPromise((resolve, reject) => { console.log('aaaa'); setTimeout(() => { resolve(1111); console.log(1111); }, 1000); }); let promise2 = new MyPromise((resolve, reject) => { console.log('bbbb'); setTimeout(() => { reject(2222); console.log(2222); }, 2000); }); let promise3 = new MyPromise((resolve, reject) => { console.log('cccc'); setTimeout(() => { resolve(3333); console.log(3333); }, 3000); }); Promise.race([promise1, promise2, promise3]).then((value) => { console.log('all value', value); }, (reason) => { console.log('all reason', reason); })
打印結果:
aaaa
bbbb
cccc
1111
all reason 1111
2222
3333
Promise.resolve
方法Promise.resolve
用來生成一個fulfilled
完成態的Promise
,通常放在整個Promise
鏈的開頭,用來開始一個Promise
鏈。
MyPromise.js
MyPromise.resolve = function(value) { let promise; promise = new MyPromise((resolve, reject) => { this.prototype.resolvePromise(promise, value, resolve, reject); }); return promise; };
test.js
let MyPromise = require('./MyPromise.js'); MyPromise.resolve(1111).then((value) => { console.log('value1', value); return new MyPromise((resolve, reject) => { resolve(2222); }) }).then((value) => { console.log('value2', value); })
打印結果:
value1 1111
value2 2222
因爲傳入的value
有多是普通值,有多是thenable
,也有多是另外一個Promise
,因此調用resolvePromise
進行解析。
Promise.reject
方法Promise.reject
用來生成一個rejected
失敗態的Promise
。
MyPromise.js
MyPromise.reject = function(reason) { return new MyPromise((resolve, reject) => { reject(reason); }); };
test.js
let MyPromise = require('./MyPromise.js'); MyPromise.reject(1111).then((value) => { console.log('value1', value); return new MyPromise((resolve, reject) => { resolve(2222); }) }).then((value) => { console.log('value2', value); }).catch(reason => { console.log('reason', reason); });
打印結果:
reason 1111
Promise.deferred
方法Promise.deferred
能夠用來延遲執行resolve
和reject
。
MyPromise.js
MyPromise.deferred = function() { let dfd = {}; dfd.promies = new MyPromise((resolve, reject) => { dfd.resolve = resolve; dfd.rfeject = reject; }); return dfd; };
這樣,你就能夠在外部經過調用dfd.resolve()
和dfd.reject()
來決議該Promise
。
Promise
鏈假設這樣一個場景,咱們有一個很長的Promise
鏈式調用,這些Promise
是依次依賴的關係,若是鏈條中的某個Promise
出錯了,就不須要再向下執行了,默認狀況下,咱們是沒法實現這個需求的,由於Promise
不管是then
仍是catch
都會返回一個Promise
,都會繼續向下執行then
或catch
。舉例:
new Promise(function(resolve, reject) { resolve(1111) }).then(function(value) { // "ERROR!!!" }).catch() .then() .then() .catch() .then()
有沒有辦法讓這個鏈式調用在ERROR!!!的後面就停掉,徹底不去執行鏈式調用後面全部回調函數呢?
咱們本身封裝一個Promise.stop
方法。
MyPromise.js
MyPromise.stop = function() { return new Promise(function() {}); };
stop
中返回一個永遠不執行resolve
或者reject
的Promise
,那麼這個Promise
永遠處於pending
狀態,因此永遠也不會向下執行then
或catch
了。這樣咱們就中止了一個Promise
鏈。
new MyPromise(function(resolve, reject) { resolve(1111) }).then(function(value) { // "ERROR!!!" MyPromise.stop(); }).catch() .then() .then() .catch() .then()
可是這樣會有一個缺點,就是鏈式調用後面的全部回調函數都沒法被垃圾回收器回收。
Promise
鏈上返回的最後一個Promise
出現錯誤看以下例子:
new Promise(function(resolve) { resolve(42) }).then(function(value) { a.b = 2; });
這裏a不存在,因此給a.b賦值是一個語法錯誤,onFulfilled
回調函數是包在try...catch
中執行的,錯誤會被catch
到,可是因爲後面沒有then
或catch
了,這個錯誤沒法被處理,就會被Promise
吃掉,沒有任何異常,這就是常說的Promise有可能會吃掉錯誤。
那麼咱們怎麼處理這種狀況呢?
方法一
就是咱們前面已經實現過的done()
。
new Promise(function(resolve) { resolve(42) }).then(function(value) { a.b = 2; }).done();
done()
方法至關於一個catch
,可是卻再也不返回Promise
了,注意done()
方法中不能出現語法錯誤,不然又沒法捕獲了。
方法二
普通錯誤監聽window
的error
事件能夠實現捕獲
window.addEventListener('error', error => { console.log(error); // 不會觸發 });
Promise沒有被onRejected()
處理的錯誤須要監聽unhandledrejection
事件
window.addEventListener('unhandledrejection', error => { console.log('unhandledrejection', error); // 能夠觸發,並且還能夠直接拿到 promise 對象 });
相關單元測試以及完整代碼能夠到個人github查看,若是對你有幫助的話,就來個star吧~