這是我參與8月更文挑戰的第8天,活動詳情查看:8月更文挑戰git
Promaise 你們再熟悉不過了,Promise 是異步編程的一種解決方案,比傳統的解決方案,回調函數和事件更合理和更強大。Promise
,簡單說就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。es6
ES6 的 Promise API 提供的方法不是不少,有些有用的方法能夠本身部署。下面介紹如何部署兩個不在 ES6 之中、但頗有用的方法。done 方法和 finally 方法。finally 方法你們可能用的比較多,done 方法相對少一點,而且如今這兩個方法出如今面試中的機率愈來愈大了,好比:github
這個多是一個問答題,也多是一個看題說結果的題目。面試
這幾個問題都是如今問的比較多的,由於 Promise 其餘的相關問題都已經被你們所熟悉了,今天我來看看這幾個不被你們熟悉的問題。編程
若是你使用過 Promise 類庫的話,你可能見過 done 方法,Promise 類庫提過Promise.prototype.done
,用 done 方法來替代 then 方法。在 Promise 規範和 Promise+ 規範中並無對 Promise.prototype.done 作任何的規範,那爲何會出現這個方法了。一切都源於那些 「消失的錯誤」 。json
咱們先回憶一下 Promise 的特色。「對象的狀態不受外界影響」,「一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果」。也回憶一下 Promise 的缺點「沒法取消 Promise
,一旦新建它就會當即執行,沒法中途取消」,「當處於 Pending
狀態時,沒法得知目前進展到哪個階段(剛剛開始仍是即將完成) 」,「若是不設置回調函數, Promise
內部拋出的錯誤,不會反應到外部」。promise
看到最後一條缺點你可能明白了,Promise 無論以then
方法或catch
方法結尾,要是最後一個方法拋出錯誤,都有可能沒法捕捉到(由於Promise內部的錯誤不會冒泡到全局)。咱們來看一個例子:babel
function JSONPromise(value) {
return new Promise(function (resolve) {
resolve(JSON.parse(value));
});
}
// 運行示例
const string = "一個不合法的json字符串";
JSONPromise(string).then(function (object) {
console.log(object);
}).catch(function(error){
// => JSON.parse拋出異常時
console.error(error);
});
複製代碼
因爲 string 這個字符串是一個不合法的 JSON 字符串,因此會解析拋出一個錯誤,而後被catch
捕捉到。正常狀況你寫了catch
方法正常捕獲,可是若是沒有寫或者漏寫了,一旦發生異常,想要查找源頭就是一個很是棘手的問題。markdown
function JSONPromise(value) {
return new Promise(function (resolve) {
resolve(JSON.parse(value));
});
}
// 運行示例
const string = "一個不合法的json字符串";
JSONPromise(string).then(function (object) {
console.log(object);
});
複製代碼
這裏可能例子比較簡單,在實際的研發過程當中 Promise 的使用確定是比這個例子複雜得多,並且代碼的異常也多是多種多樣的。可是,因爲 Promise 的 try-catch 機制,這個問題可能就會在 Promise 的內部消化掉,也就是所謂的消失的錯誤。固然有的同窗會說我每次調用進行 catch
處理不就行了,這樣無疑是最好的。可是並非每個人都像你這樣優秀😁。若是在實現的過程當中出現了這個例子中的錯誤的話,那麼進行錯誤排除的工做也會變得困難。dom
消失的錯誤還有一個專業名詞unhandled rejection,意思就是 Rejected 時沒有找到相應處理的意思。在不少 Promise 類庫中對unhandled rejection都會有相應的處理。例如:
它的實現代碼至關簡單。
Promise.prototype.done = function (onFulfilled, onRejected) {
this.then(onFulfilled, onRejected)
.catch(function (reason) {
// 拋出一個全局錯誤
setTimeout(() => { throw reason }, 0);
});
};
複製代碼
從上面代碼可見,done
方法的使用,能夠像then
方法那樣用,提供Fulfilled
和Rejected
狀態的回調函數,也能夠不提供任何參數。但無論怎樣,done
都會捕捉到任何可能出現的錯誤,並向全局拋出。若是嚴格一點,也能夠這樣寫:
"use strict";
if (typeof Promise.prototype.done === "undefined") {
Promise.prototype.done = function (onFulfilled, onRejected) {
this.then(onFulfilled, onRejected).catch(function (error) {
setTimeout(function () {
throw error;
}, 0);
});
};
}
複製代碼
done
並不返回 Promise 對象,因此在done
以後並不能在使用catch
。done 的錯誤是直接拋出去的,並不會進行 Promise 的錯誤處理。Promise具備強大的錯誤處理機制,而done
則會在函數中跳過錯誤處理,直接拋出異常。
講完 done 方法你已經瞭解到爲何會有 done 的出現,若是本身實現一個,接下來在來看看 finally 方法。
finally
方法用於指定無論 Promise 對象最後狀態如何,都會執行的操做。它與done
方法的最大區別,它接受一個普通的回調函數做爲參數,該函數無論怎樣都必須執行。
server.listen(0)
.then(function () {
// run test
})
.finally(server.stop);
複製代碼
.then(f, f)
?其實本質上 finally(func)與 then(func,func)相似,可是在一些關鍵方面有所不一樣:
內聯建立函數時,您能夠傳遞一次,而沒必要被強制聲明兩次或爲其建立變量
因爲沒有可靠的方法來肯定 Promise 是否已兌現,所以 finally 回調將不會收到任何參數。正是這種用例適用於您不關心拒絕緣由或實現價值,所以不須要提供它的狀況。
與 Promise.resolve(2).then(() => {}, () => {}) (將使用未定義的解析)不一樣,Promise.resolve(2).finally(() => {}) 將用2.解決
一樣,與Promise.reject(3).then(() => {}, () => {})(將使用未定義的解析)不一樣,Promise.reject(3).finally(() => {})將被拒絕3。
它的實現也很簡單。
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};
複製代碼
上面代碼中,無論前面的Promise是fulfilled
仍是rejected
,都會執行回調函數callback
。
finally 方法本質是一個 then 方法,因此在實現方法中要調用 then 方法入參是一個函數,須要在 then 方法中執行這個函數
使用 Promise.resolve 會等入參的函數執行完再返回結果,並將上一個 then 的 value 返回 reject 方法中須要拋出錯誤信息。
在討論這個問題以前,咱們先把 Promise.prototype.finally 轉換爲 ES5 是什麼樣的。
"use strict";
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => {
throw reason;
}));
};
複製代碼
在線轉換:es6console.com/,babeljs.io/repl
你是否是明白了什麼,要這麼寫的緣由是在於,finally
其實並不必定是這個promise
鏈的最後一環,相對而言,其實done
纔是。由於finally
可能以後還有then
和catch
等等,因此其必需要返回一個promise
對象。是否是瞬間秒懂。
今天對 Promise 的 done 方法和 finally 方法進行了一個介紹,也從原理的角度爲你們手寫了它們的實現,這兩個方法看完也能夠在項目中使用起來,可是注意兼容性,並非全部地方都能使用。但願今天的文章對你有幫助。
若是你以爲寫得不錯,幫忙點個贊吧。