在 Promise 出現之前,咱們處理一個異步網絡請求,需求大概是這樣:咱們須要根據第一個網絡請求的結果,再去執行第二個網絡請求,再根據第二個網絡請求的結果去執行第三個請求~,需求是永無止境的,因而乎出現了以下代碼:javascript
請求1(function(){
// 一些其餘操做
請求2(function(請求1結果){
// 一些其餘操做
請求3(function(請求2結果){
// 一些其餘操做
請求4(function(請求3結果){
// 一些其餘操做
請求5(function(請求4結果){
// 一些其餘操做
請求6(function(請求5結果){
// 一些其餘操做
...
})
})
})
})
})
})
複製代碼
這就是所謂的回調地獄,回調地獄帶來的負面做用有如下幾點:java
這時,就有人思考了,能不能用一種更加友好的代碼組織方式,解決異步嵌套的問題。因而 Promise 規範誕生了,promise採用鏈式調用,很好地解決了回調地獄的痛點。編程
咱們使用Promise常規寫法來實現上面的異步網絡請求,代碼以下:數組
new Promise(請求1)
.then(請求2(請求結果1))
.then(請求3(請求結果2))
.then(請求4(請求結果3))
.then(請求5(請求結果4))
.then(請求6(請求結果5))
.catch(處理異常(異常信息))複製代碼
咱們不難發現,Promise 的寫法更爲直觀,而且可以在外層捕獲異步函數的異常信息。promise
Promise 是異步編程的一種解決方案,比傳統的解決方案【回調函數】和【事件】更合理、更強大。它由社區最先提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了Promise對象。Promise說得通俗一點就是一種寫代碼的方式,而且是用來寫JavaScript編程中的異步代碼的。bash
三種狀態:網絡
只有異步操做的結果才能肯定當前處於哪一種狀態,任何其餘操做都不能改變這個狀態,這也是Promise(承諾)的由來。dom
Promise對象的狀態改變,只有兩種可能:異步
這兩種狀況只要發生,狀態就凝固了,不會再變了,這時就稱爲resolved(已定型)異步編程
- 沒法取消Promise,一旦新建它就會當即執行,沒法中途取消
- 若是不設置回調函數(沒有捕獲錯誤),Promise內部拋出的錯誤,不會反應到外部
- 當處於pending狀態時,沒法得知目前進展到哪個階段(剛剛開始仍是即將完成)
咱們先把Promise打印出來,會發現Promise是一個構造函數,本身身上有all、reject、resolve、race等方法,原型上有then、catch、finally等方法。
【1】Promise.prototype.constructor()
它的基本用法以下:
let promise = new Promise((resolve, reject) => {
// 在這裏執行異步操做
if (/*異步操做成功*/) {
resolve(success)
} else {
reject(error)
}
})複製代碼
Promise接收一個函數做爲參數,函數裏有resolve和reject兩個參數:
- resolve方法的做用是將Promise的pending狀態變爲fulfilled,在異步操做成功以後調用,能夠將異步返回的結果做爲參數傳遞出去。
- reject方法的做用是將Promise的pending狀態變爲rejected,在異步操做失敗以後調用,能夠將異步返回的結果做爲參數傳遞出去。
他們之間只能有一個被執行,不會同時被執行,由於Promise只能保持一種狀態。
【2】Promise.prototype.then()
Promise實例肯定後,能夠用then方法分別指定fulfilled狀態和rejected狀態的回調函數。它的基本用法以下:
promise.then((success) => {
// 異步操做成功在這裏執行
// 對應於上面的resolve(success)方法
}, (error) => {
// 異步操做失敗在這裏執行
// 對應於上面的reject(error)方法
})
// 還能夠寫成這樣 (推薦使用這種寫法)
promise.then((success) => {
// 異步操做成功在這裏執行
// 對應於上面的resolve(success)方法
}).catch((error) => {
// 異步操做失敗在這裏執行
// 對應於上面的reject(error)方法
})複製代碼
then(onfulfilled,onrejected)方法中有兩個參數,兩個參數都是函數,第一個參數執行的是resolve()方法(即異步成功後的回調方法),第二參數執行的是reject()方法(即異步失敗後的回調方法)(第二個參數可選)。它返回的是一個新的Promise對象。
【3】Promise.prototype.catch()
catch方法是.then(null,onrejected)的別名,用於指定發生錯誤時的回調函數。做用和then中的onrejected同樣,不過它還能夠捕獲onfulfilled拋出的錯,這是onrejected所沒法作到的:
function createPromise(p, arg) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (arg === 0) {
reject(p + ' fail')
} else {
resolve(p + ' ok')
}
}, 0);
})
}
createPromise('p1', 1).then((success) => {
console.log(success) // p1 ok
return createPromise('p2', 0)
}).catch((error) => {
console.log(error) // p2 fail
})
createPromise('p1', 1).then((success) => {
console.log(success) // p1 ok
return createPromise('p2', 0)
}, (error) => {
console.log(error) // Uncaught (in pomise) p2 fail
})
複製代碼
Promise錯誤具備"冒泡"的性質,若是不被捕獲會一直往外拋,直到被捕獲爲止;而沒法捕獲在他們後面的Promise拋出的錯。
【4】Promise.prototype.finally()
finally方法用於指定無論Promise對象最後狀態如何,都會執行的操做。該方法是 ES2018 引入的標準:
createPromise('p1', 0).then((success) => {
console.log(success)
}).catch((error) => {
console.log(error) // p1 fail
}).finally(() => {
console.log('finally') // finally
})
createPromise('p1', 1).then((success) => {
console.log(success) // p1 ok
}).catch((error) => {
console.log(error)
}).finally(() => {
console.log('finally') // finally
})複製代碼
finally方法不接受任何參數,故可知它跟Promise的狀態無關,不依賴於Promise的執行結果。
【5】Promise.all()
Promise.all方法接受一個數組做爲參數,但每一個參數必須是一個Promise實例。Promise的all方法提供了並行執行異步操做的能力,而且在全部異步操做都執行完畢後才執行回調,只要其中一個異步操做返回的狀態爲rejected那麼Promise.all()返回的Promise即爲rejected狀態,此時第一個被reject的實例的返回值,會傳遞給Promise.all的回調函數:
function createPromise(p, arg) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (arg === 0) {
reject(p + ' fail')
} else {
resolve(p + ' ok')
}
}, 0);
})
}
// test: 兩個Promise都成功
Promise.all([createPromise('p1', 1), createPromise('p2', 1)])
.then((success) => {
console.log(success) // ['p1 ok', 'p2 ok']
}).catch((error) => {
console.log(error)
})
// test: 其中一個Promise失敗
Promise.all([createPromise('p1', 0), createPromise('p2', 1)])
.then((success) => {
console.log(success)
}).catch((error) => {
console.log(error) // p1 fail
})
// test: 兩個Promise都失敗
Promise.all([createPromise('p1', 0), createPromise('p2', 0)])
.then((success) => {
console.log(success)
}).catch((error) => {
console.log(error) // p1 fail 只打印第一個失敗的異步操做信息
})
複製代碼
【6】Promise.race()
Promise的race方法和all方法相似,都提供了並行執行異步操做的能力。顧名思義,race就是賽跑的意思,意思就是說Promise.race([p1, p2, p3])裏面哪一個結果得到的快,就返回那個結果,無論結果自己是成功狀態仍是失敗狀態,如下就是race的執行過程:
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('failed')
}, 500)
})
Promise.race([p1, p2]).then((success) => {
console.log(success)
}).catch((error) => {
console.log(error) // failed
})
複製代碼
【7】Promise.resolve()
有時須要將現有對象轉爲 Promise 對象Promise.resolve()方法就起到這個做用。
Promise.resolve('foo')
// 等價於
new Promise(resolve => resolve('foo'))複製代碼
【8】Promise.reject()
Promise.reject()方法也會返回一個新的 Promise 實例,該實例的狀態爲rejected。
const p = Promise.reject('出錯了');
// 等同於
const p = new Promise((resolve, reject) => reject('出錯了'))複製代碼
Promise 翻譯成中文爲「承諾, 諾言」, 例如: 你承諾這個月掙錢了給你老婆買一個包, 那麼你先去掙錢, 等掙錢了就馬上給老婆買包, 實現你的諾言, 沒掙到錢就立馬道歉。換成代碼就是:
// 買包就是一個Promise,Promise的意思就是承諾
// 這時候老公給老婆一個承諾
// 在將來的一個月,無論掙沒掙到錢,都會給老婆一個答覆
let buyBag = new Promise((resolve, reject) => {
// Promise 接受兩個參數
// resolve: 異步事件成功時調用(掙到錢)
// reject: 異步事件失敗時調用(沒掙到錢)
// 模擬掙錢機率事件
let result = function makeMoney() {
return Math.random() > 0.5 ? '掙到錢' : '沒掙到錢'
}
// 下面老公給出承諾,無論掙沒掙到錢,都會給老婆一個答覆
if (result == '掙到錢')
resolve('我買包了')
else
reject('很差意思,我這個月沒掙到錢')
})
buyBag().then(res => {
// 返回 "我買包了"
console.log(res)
}).catch(err => {
// 返回 "很差意思,我這個月沒掙到錢"
console.log(err)
})複製代碼
解釋一下
第一段調用了Promise構造函數,第二段是調用了promise實例的.then方法
1. 構造實例
2. 調用.then