Promis.resolve()
- 如果向Promise.resolve()傳遞一個非promise非thenalbe的立即值,就會得到一個用這個值填充的promise。
- 如果向Promise.resolve()傳遞一個promise,就只會返回同一個promise
- 如果向Promise.resolve()傳遞了一個非Promise的thenable值,前者會試圖展開這個值,而且展開過程中會持續到提取出一個具體的非類Promise的最終值。
假設我們要呼叫一個工具 foo(..) ,且並不確定得到的返回值是否是一個可信任的行為良好的 Promise,但我們可以知道它至少是一個 thenable。Promise.resolve(..) 提供了可信任的 Promise 封裝工具
// 不要只是這麼做:
foo( 42 )
.then( function(v){
console.log( v );
} );
// 而要這麼做:
Promise.resolve( foo( 42 ) )
.then( function(v){
console.log( v );
} );
複製程式碼
對於用 Promise.resolve(..) 為所有函式的返回值(不管是不是 thenable)都封裝一層。另一個好處是,這樣做很容易把函式呼叫規範為定義良好的非同步任務。如果 foo(42) 有時會返回一個立即值,有時會返回 Promise,那麼 Promise.resolve( foo(42) ) 就能夠保證總會返回一個 Promise 結果
預設錯誤處理函式
如果一個promise鏈中的上一級promise丟擲一個錯誤,但是下一級promise並沒有配置錯誤處理函式,promise會有一個預設處理函式來處理這個錯誤,即吧這個錯誤重新丟擲給下一級promise的錯誤處理函式。
如果一直找到最後一級的promise都沒有錯誤處理函式,那麼promise會在控制檯打出一個錯誤資訊,並且這個錯誤是有可能中斷nodejs程式的。
DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
複製程式碼
(在瀏覽器環境貌似並不會中斷程式)
Promise鏈
- 呼叫promise的then方法,會自建立一個新的Promise,並且決議的值就是then中resolve/reject函式的返回值;
- 如果resolve/reject函式返回的是一個Promise,那麼會等待這個Promise的決議結果,並將決議結果作為當前then方法生成的Promise的決議值;
var a = new Promise(function(resolve, reject) {
resolve(10)
}).then(
res => {
return b
}
).then(res => {
console.log('1err:' + res)
return res
}).then(res => {
console.log('1res:' + res)
return res
}, err => {
console.log('2err:' + err)
})
var b = new Promise(function(resolve, reject) {
resolve(123123123123)
})
複製程式碼
Promise構造器中兩個回撥函式
reject很好理解就是處理錯誤,那麼resolve為什麼不用fulfill呢?貌似應該是處理成功請求?實際上resolve的意思是決議,也就是可能是成功的結果也可能是失敗的結果;比如所給resolve傳入thenable或者一個真正的Promise,這個時候決議的結果取決於返回值的決議結果,所以說resolve返回的有可能是成功也有可能是失敗;
var rejectedPr = new Promise( function(resolve,reject){
// 用一個被拒絕的promise完成這個promise
resolve( Promise.reject( "Oops" ) );
} );
rejectedPr.then(
function fulfilled(){
// 永遠不會到達這裡
},
function rejected(err){
console.log( err ); // "Oops"
}
);
複製程式碼
前面提到的 reject(..) 不會像 resolve(..) 一樣進行展開。如果向 reject(..) 傳入一個 Promise/thenable 值,它會把這個值原封不動地設定為拒絕理由。後續的拒絕處理函式接收到的是你實際傳給 reject(..) 的那個 Promise/thenable,而不是其底層的立即值。
回撥函式簡介
回撥函式就是一個通過函式指標呼叫的函式。如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用來呼叫其所指向的函式時,我們就說這是回撥函式。回撥函式不是由該函式的實現方直接呼叫,而是在特定的事件或條件發生時由另外的一方呼叫的,用於對該事件或條件進行響應
而then方法的兩個回撥函式應該使用onFulfilles/fulfilled和onRejected/rejected因為對於then來說第一個回撥只能處理正確的結果。
(fulfilled the promis 兌現承諾)
Promise錯誤處理函式的細節
通常我們有兩種寫法來捕獲錯誤:
var a = new promise(function(resolve, reject){})
// 在then方法的內部第二個回撥捕獲
a.then(res => {
}, err => {
})
// 在catch方法中捕獲
a.then(res => {}).catch(err => {})
複製程式碼
那麼這兩種方法有什麼不同? 實際上第二種方法是包含第一種方法的,只不過then方法中的第二個回撥被預設執行了,而預設的錯誤處理回撥只是簡單的丟擲錯誤,然後就會被catch方法捕獲; 這裡就有一個問題,如果a的決議是resolve,那麼就會呼叫then的第一個成功回撥,如果這個成功回撥中的具體程式碼中發生了錯誤,第一種方法是無法捕獲的,而第二種方法是可以捕獲的。
var a = new promise(function(resolve, reject){
resolve(12)
})
// 在then方法的內部第二個回撥捕獲
a.then(res => {
aaa() // 這裡aaa是不存在的會發生錯誤
}, err => {
console.log(err) // 不會走這裡,所以錯誤不會被捕獲
})
// 在catch方法中捕獲
a.then(res => {
aaa() // 這裡aaa是不存在的會發生錯誤
}).catch(err => {
console.log(err) // 這裡會捕獲then方法中的所有錯誤,自然也能捕獲aaa導致的錯誤
})
複製程式碼
Promise.race()的應用場景
實際上Promise.race([p1, p1, p3])這種模式在程式設計中通常是一種被稱為競態的bug,但是它還是有一定的應用場景:
- 請求超時處理
var p1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(2222)
}, 2000)
})
// 定義一個超時函式,如果p1超過這個時間還未決議,就認為這個請求超時了。
var timeoutPromise = new Promise(function(resolve, reject) {
setTimeout(function() {
reject('timeout')
}, 1000)
})
Promise.race([p1, timeoutPromise]).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
複製程式碼
有時候我們可能會想,Promsie.race()中那些別丟棄(比賽中失敗)的Promise哪裡去了?當然最後會被垃圾回收,但是,Promsie不能別停止,它們依舊會完成自己的任務直到決議,目前來看只是被默默的丟棄了,像空氣一樣,或許在以後的規範中會新增類似與finally之類的回撥函式用於處理這些被遺棄的孩子。