$.ajax({
success: (res) => {
$.ajax({
success: (res) => {
$.ajax({
success: (res) => {
//...
}
})
}
})
}
})
複製代碼
這就是典型的回調地獄,不只代碼臃腫,可讀性差,並且耦合度太高,不易維護。代碼沒法複用,還容易隱藏bug。前端
Promise規範的出現就是爲了解決這種問題。ajax
這裏強調一下,Promise是一種解決方案,它是一種規範。數組
ES6原生提供了Promise對象,在平常開發中常常會接觸到Promise相關操做,本文將介紹Promise的使用方法及相關原理,但願能有所幫助。promise
Promise有三種狀態:異步
Promise的特色是狀態只能由Pending轉換爲Fulfilled或Rejected,而且一旦改變再也不更改。函數
new Promise((resolve,reject) => {
/*executor*/
})
複製代碼
Promise是一個帶有resolve
,reject
兩個參數的構造函數(executor
)。這個構造函數在Promise建立的時候當即執行,resolve和reject兩個函數在被調用時,分別將Promise的狀態更改成fulfilled(成功態)和rejected(失敗態),並傳遞成功的值或失敗的緣由。executor
內部一般會執行一些異步操做,當異步操做執行完畢時,可根據執行結果相應地調用resolve或reject(可能成功可能失敗)。若是executor拋出一個錯誤,那麼該Promise也將轉爲失敗態。測試
Promise原型上還具備then和catch方法,調用後返回一個Promise實例,所以能夠被鏈式調用。網站
每個Promise實例都帶有一個then方法,該方法有兩個參數(onFulfilled
,onRejected
),分別爲Promise成功和失敗時的回調。ui
let p = new Promise((resolve,reject) => {
resolve('success');
})
p.then(value => {
console.log(value); //success
}).then(value => {
console.log(value); //沒有返回值時,undefined
})
複製代碼
當返回了一個值或者一個成功態的Promise時,then返回的Promise都是成功態,若沒有返回值時也是成功態,而回調函數的參數值爲undefind
。spa
當拋出一個錯誤或者返回一個失敗態Promise,then返回的Promise都是失敗態。
let p = new Promise((resolve,reject) => {
reject('fail');
})
p.then(() => {
},err => {
console.log(err); //fail
});
複製代碼
then方法傳遞的成功/失敗函數,這兩個函數能夠返回一個promise;若是返回的是一個promise,則該promise狀態將做爲下一次then的結果。
let p = new Promise((resolve,reject) => {
resolve('success')
});
let p2 = new Promise((resolve,reject) => {
resolve('success2')
})
p.then(value => {
return p2; //p2的成功態將做爲下一次then的狀態
}).then(value => {
console.log(value)
})
複製代碼
Promise.prototype.catch
方法返回也是一個Promise,使用方式同then方法,用於處理異常捕獲狀況。該方法捕獲屬於就近捕獲原則,離錯誤最近的一個catch將會捕獲錯誤並進行處理,後續將再也不捕獲。
若失敗狀態被then方法中失敗函數回調觸發,則catch方法將不進行捕獲。
let p1 = new Promise((resolve,reject) => {
resolve('success');
});
p1.then(value => {
throw 'a new error';
}).catch(e => {
console.log(e); //a new error
}).then(() => {
console.log('after a catch the chain is restored');
})
複製代碼
Promise.all
方法返回一個新的Promise對象,在參數對象中全部的promise對象都成功時纔會觸發成功態,一旦有任意一個對象失敗則當即觸發該Promise對象的失敗態。當觸發成功態後,會把一個包含全部Promise返回值的數組按順序返回。若是觸犯失敗態,則會把第一個失敗的Promise對象錯誤信息返回。
let p1 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('p1')
},0)
});
let p2 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('p2')
},0)
});
let promise = Promise.all([p1,p2]).then(value => {
console.log(value); //['p1','p2']
})
複製代碼
Promise.race方法是把參數中任意第一個完成狀態轉換的Promise做爲成功或失敗狀態,並返回該Promise對象。
let p1 = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('p1')
},1000)
});
let p2 = new Promise((resolve,reject) => {
setTimeout(() => {
reject('p2')
},0)
});
Promise.race([p1,p2]).then((value) => {
console.log(value)
},(err) => {
console.log(err); //p2
})
複製代碼
Promise.resolve(value)返回一個成功態,而且將value傳遞給對應的then方法;
Promise.reject(reason)返回一個失敗態,而且將reason傳遞個對應的then方法。
優勢 | 缺點 |
---|---|
解決回調 | 沒法監測進行狀態 |
鏈式調用 | 新創建即執行且沒法取消 |
減小嵌套 | 內部錯誤沒法拋出 |
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
const promise2 = promise1.then(() => {
return 'an error'
})
console.log('promise1', promise1)
console.log('promise2', promise2)
setTimeout(() => {
console.log('promise1', promise1)
console.log('promise2', promise2)
}, 2000)
複製代碼
const promise = new Promise((resolve, reject) => {
resolve('success1')
reject('error')
resolve('success2')
})
promise.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
複製代碼
Promise.resolve(1)
.then((res) => {
console.log(res)
return 2
})
.catch((err) => {
return 3
})
.then((res) => {
console.log(res)
})
複製代碼
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('executor')
resolve('success')
}, 1000)
})
const start = Date.now()
promise.then((res) => {
console.log(res, Date.now() - start)
})
promise.then((res) => {
console.log(res, Date.now() - start)
})
//Promise的構造函數只執行一次,而且當即執行;promise的then方法能夠被屢次調用,每次都返回新的promise實例。
複製代碼
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('executor')
resolve('success')
}, 1000)
})
const promise2 = promise.then(res => {
console.log(res);
})
promise2.then(res => {
console.log(res)
})
複製代碼
Promise.resolve()
.then(() => {
return new Error('error!!!')
})
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
複製代碼
Promise.resolve()
.then(() => {
throw new Error('error'); //return Promise.reject('error')
})
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
複製代碼
then或catch方法返回一個錯誤對象並不會被捕獲,只有在拋出錯誤或返回失敗態時纔會引發捕獲。
const promise = Promise.resolve()
.then(() => {
return promise
})
promise.catch(e => {
console.log(e)
})
//[TypeError: Chaining cycle detected for promise #<Promise>]
複製代碼
返回值不能是promise實例自己,由於此時實例並未構建完成,形成死循環,所以拋出類型錯誤。
process.nextTick(() => {
console.log('nextTick')
})
Promise.resolve()
.then(() => {
console.log('then')
})
setImmediate(() => {
console.log('setImmediate')
})
console.log('end')
複製代碼
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
複製代碼
then和catch方法的參數是一個函數,若非函數則發生穿透
該網站介紹了若須要實現Promise須要實現的規範方法。當多個庫實現本身的Promise類時,爲了能實現相應的效果,該規範提供了實現標準,並提供了promises-aplus-tests
測試包測試實現能力。
後續將推出文章介紹如何實現本身的Promise類,敬請期待!