第一部分、什么是Promise ?
Promise是ES6中提供的一個異步編程的解決方案,Promise本身是一個構造函數
typeof Promise // function
一般情況下 我們在開發中會使用 new Promise() 調用構造函數,創建一個新的Promise對象, Promise對象有兩個特點
1、對象的狀態不受外界影響。Promise
對象是一個異步操作,有三種狀態:pending
(進行中)、fulfilled
(已成功)和rejected
(已失敗)。
只有異步操作的結果,可以決定Promise是哪一種狀態,任何其他操作都無法改變這個狀態
2、一旦Promise狀態改變,就不會再有變化,任何時候都可以得到這個結果。Promise對象的狀態改變,只有兩種可能:從pending變為fulfilled 或者 從pending變為rejected。只要這兩種情況發生,狀態就不會再變了,會一直保持這個結果,這時就稱為 resolved(已定型)。如果改變已經發生了,你再對Promise對象添加回調函數,也會立即得到這個結果。這與事件(Event)不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的
使用Promise的好處,就是在處理異步程序時,將異步操作隊列化,按照期望的順序執行,返回符合預期的結果,這樣即使是多重異步操作,也可以方便的使用Promise進行鏈式調用
3、Promise
也有一些缺點。
首先,無法取消Promise
,一旦新建它就會立即執行,無法中途取消。其次,如果不設置回調函數,Promise
內部拋出的錯誤,不會反應到外部。第三,當處於pending
狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)
第二部分、循序漸進的介紹Promise的用法
1、最簡單的用法
const p = new Promise((resolve, reject) => { // 在這里執行一個異步操作吧 setTimeout(function(){ console.log(1234) resolve(1234) }, 3000) }) // 創建這個Promise之后, 3秒后 會控制台輸出1234,並且3秒后得到了一個結果,異步程序返回了1234這個結果,那么 p.then((res) => { console.log(res) }) // 控制台會輸出1234 ,promise的then方法 會再次返回一個promise,不過值默認undefined p.then((res) => { console.log(res) }).then((res)=>{ console.log(res) }) // 1234, undefined
2、reject的用法
const p2 = new Promise((resolve, reject)=>{ // 執行一個異步操作 setTimeout(function(){ console.log(1024) reject(1024) }, 3000) }) p2.then(res=>{ console.log(res) }).catch(res=>{ console.error(res) }) // 3秒之后 輸出錯誤 1024
3、all的用法, 多個異步操作進行數組形式的返回值處理,當所有的異步都resolve時,可以執行then操作,當其中一個或多個reject時,所有的異步都會停止調用,直接返回最早發生錯誤的那個結果
const pa = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pa') resolve('pa') },3000) }) const pb = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pb') resolve('pb') },4000) }) const pc = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pc') resolve('pc') },5000) }) const pd = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pd') reject('pd') },5000) }) Promise.all([pa, pb, pc]).then((res)=>{ console.log('成功' + res) }) // 3秒后 pa 4秒后pb 5秒后 pc pd 然后輸出成功pa,pb,pc Promise.all([pa, pb, pd]).then((res)=>{ console.log('成功' + res) }).catch(err=>{ console.log('失敗' + err) }) // 輸出失敗pd
4、race的用法, 語法和all()一樣,但是返回值有所不同,race根據傳入的多個Promise實例,只要有一個實例resolve或者reject,就只返回該結果,其他實例不再執行,也就是說多個異步程序,只返回響應最快的那個異步程序,不論成功或者失敗,就把最快響應的返回值返回,后面的異步程序不再執行
const pa = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pa') resolve('pa') },3000) }) const pb = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pb') resolve('pb') },4000) }) const pc = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pc') resolve('pc') },5000) }) const pd = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pd') reject('pd') },5000) }) const pe = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pe') reject('pe') },2000) }) const pf = new Promise((resolve, reject)=>{ setTimeout(function(){ console.log('pf') resolve('pf') },2000) }) Promise.race([pa, pe, pf]).then(res=>{ console.log('成功' + res) }).catch(res=>{ console.log('失敗' + res) }) Promise.race([pd, pe]).then().catch(res=>{ console.log('失敗' + res) }) // 2秒后 輸出 失敗 + pe // 2秒后 輸出 失敗 + pe
5、resolve方法,
Promise的resolve方法,用於將非Promise類型的對象,轉為Promise對象,這樣就可以調用Promise原型對象上的方法了
Promise.resolve(x) 上面的寫法等價於 new Promise(resolve => { resolve(x)})
resolve的參數分為幾種不同的情況:
(1)沒有參數,如果沒有參數,這直接返回一個resolved狀態的Promise對象
const p = Promise.resolve() 相當於 const p = new Promise(resolve => { resolve undefined }) p.then(res=>{ console.log(res) }) // 輸出 undefined
(2) 參數是一個不具有then方法的對象,或者是一個基數數據類型的值,則Promise.resolve
方法返回一個新的 Promise 對象,狀態為resolved,值為指定值
const p = Promise.resolve('pro') p.then((x) =>{ console.log(x) }) // 輸出 'pro'
(3) 參數是一個具有then方法的對象,Promise.resolve
方法會將這個對象轉為 Promise 對象,然后就立即執行thenable
對象的then
方法。
const obj = { then: function(resolve, reject) { resolve(100) } } const p = Promise.resolve(obj) p.then((res) => { console.log(res) }) // 輸出 100
(4)參數是一個Promise對象,那么將原封不動的返回這個Promise對象
6、reject方法
reject與resolve方法基本類似,但是要注意一種情況,就是當參數是一個thenable對象時
const thenable = { then(resolve, reject) { reject('出錯了'); } }; Promise.reject(thenable) .catch(e => { console.log(e === thenable) }) // true
7、finally方法,finally
方法用於指定不管 Promise 對象最后狀態如何,都會執行的操作。該方法是 ES2018 引入標准的。
finally
方法的回調函數不接受任何參數,這意味着沒有辦法知道,前面的 Promise 狀態到底是fulfilled
還是rejected
。這表明,finally
方法里面的操作,應該是與狀態無關的,不依賴於 Promise 的執行結果。finally
本質上是then
方法的特例。
const p = new Promise((resolve, reject)=>{ const a = Math.ceil( Math.random() * 10 ) setTimeout(function(){ if(a > 5) { resolve(a) } else { reject(a) } },3000) }) p.then(res=>{ console.log(res) }).catch(err=>{ console.error(err) }).finally(()=>{ console.log('結束') }) // 無論是resolve 還是 reject都會 執行 finally // finally的 polyfill也很簡單 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 }) ); };