Promise
1.Promise的前置知識
-
程式(廠房)
- 程式的執行環境
-
執行緒(工人)
-
執行緒是實際進行運算的東西
-
同步
- 通常情況程式碼都是自上向下一行一行執行的
- 前邊的程式碼不執行後邊的程式碼也不會執行
- 同步的程式碼執行會出現阻塞的情況
- 一行程式碼執行慢會影響到整個程式的執行
-
解決同步的問題:
-
java python
- 透過多執行緒來解決,但是一般消耗資源比較多
-
node.js
-
透過非同步方式來解決
我們可以這麼理解:客人就好比我們請求的資料,服務員就好比客戶端,廚師就好比伺服器,我們現在客人點菜,服務員接收到菜的名稱資訊,給廚師說,廚師開始做,廚師在做的時候,客人一直等,不能幹其他的事情,這就是同步,只能幹一件事,我們現在利用非同步的方式,可以讓客人在課桌上等著菜來,也不影響服務員接收下一個客人的點菜,這樣就可以很好的處理同步所帶來的堵塞問題
-
-
-
非同步
- 一段程式碼的執行不會影響到其他的程式
- 非同步的問題:
-
非同步的程式碼無法透過
return
來設定返回值 -
特點:
-
不會阻塞其他程式碼的執行
-
需要透過回撥函式來返回結果
function sum(a, b, cb) { setTimeout(() => { cb(a + b) //呼叫箭頭函式,把結果作為回撥函式的引數 }, 1000) } sum(123, 456, (result)=>{ console.log(result) })
-
-
基於回撥函式的非同步帶來的問題
-
程式碼的可讀性差
-
可除錯性差(造成回撥地獄)
sum(123, 456, (result)=>{ sum(result, 7, (result)=>{ sum(result, 8, result => { sum(result, 9, result => { sum(result, 10, result => { console.log(result) }) }) }) }) })
-
-
解決問題:
- 需要一個東西,可以代替回撥函式來給我們返回結果
Promise
橫空出世- Promise是一個可以用來儲存資料的物件
- Promise儲存資料的方式比較特殊,這種特殊的方式使得Promise可以用來儲存非同步呼叫的資料
- Promise是一個可以用來儲存資料的物件
2.Promise介紹
非同步呼叫必須要透過回撥函式來返回資料,當我們進行一些複雜的呼叫時,會出現回撥地獄
問題:
非同步必須透過回撥函式來返回結果,回撥函式增加就不容易處理
- Promise
- Promise可以幫助我們解決非同步中的回撥函式的問題
- Promise就是一個用來儲存資料的容器
- 它擁有著一套特殊的儲存資料的方式
- 這個方式使得它裡面可以儲存非同步呼叫的結果
-
建立Promise
-
建立Promise時,建構函式中需要一個函式作為引數
-
Promise建構函式的回撥函式,它會在建立Promise時呼叫,呼叫時會有兩個引數傳遞進去
const promise = new Promise((resolve, reject)=>{ // resolve 和 reject 是兩個函式,透過這兩個函式可以向Promise中儲存資料 // resolve 在執行正常的時候儲存資料, reject 是在執行錯誤的時候儲存資料 resolve('我是正常執行的時候呼叫的') reject('我是錯誤執行的時候呼叫的') //透過函式來訪問Promise中新增資料,好處就是可以用來新增非同步呼叫的資料 setTimeout(()=>{ resolve('非同步中呼叫資料') },2000) throw new Error('出錯了,呼叫的是reject') })
-
-
從Promise中讀取資料
-
可以透過Promise的例項方法
then
來讀取Promise中儲存的資料 -
then需要兩個回撥作為引數,回撥函式來獲取Promise中的資料
-
透過resolve儲存的資料,會呼叫第一函式返回,可以在第一個函式中編寫處理資料的程式碼
-
透過reject儲存資料或者出現異常時,會呼叫第二個函式返回,可以在第二個函式中編寫處理異常的程式碼
promise.then((result)=>{ console.log('1',result) },(reson)=>{ console.log('2',reason) })
-
-
-
Promise中維護了兩個隱藏屬性:
- PromiseResult
- 用來儲存資料
- PromiseState
- 記錄Promise的狀態(三種狀態)
pending
(進行中)fulfilled
(完成)透過resolve儲存資料時rejected
(拒絕,出錯了)出錯了或透過reject儲存資料時
- state只能修改一次,修改以後永遠不會在變
- 記錄Promise的狀態(三種狀態)
- 流程:
- 當Promise建立時,PromiseState初始值為pending
- 當透過resolve儲存資料時 PromiseState 變為fulfilled(完成)
- PromiseResult變為儲存的資料
- 當透過reject儲存資料或出錯時 Promise 變為rejected(拒絕)
- PromiseResult變為儲存的資料 或 異常物件
- 當透過resolve儲存資料時 PromiseState 變為fulfilled(完成)
- 當我們透過then讀取資料時,相當於為Promise設定了回撥函式
- 如果PromiseState變為fulfilled,則呼叫then的第一個回撥函式來返回資料
- 如果PromiseState變為rejected。則呼叫then的第二個回撥函式來返回資料
- 當Promise建立時,PromiseState初始值為pending
const promise2 = new Promise((resolve, reject) => { resolve("哈哈") }) // console.log(promise2) promise2.then(result => { console.log(result) }, reason => { console.log("出錯了") })
- PromiseResult
-
catch()
用法和then類似,但是隻需要一個回撥函式作為引數-
catch() 中的回撥只會在Promise被拒絕時才會呼叫
-
catch() 相當於 then(null, reason=>{})
-
catch() 就是一個專門處理Promise異常的方法
promise2.catch(reason => { console.log(222222) })
-
-
finally()
-
無論是正常儲存資料還是出現異常了,finally總會執行
-
但是finally的回撥函式中不會接收到資料
-
finally()通常用來編寫一些無論成功與否都要執行的程式碼
promise2.finally(()=>{ console.log("沒有什麼能夠阻擋我執行的!") })
-
3.Promise詳解
3.1Promise用法
Promise就是一個用來儲存資料物件
,但是由於Promise存取的方式的特殊,所以可以直接將非同步呼叫的結果儲存到Promise中
function sum(a, b) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(a + b)
}, 1000)
})
}
//回撥地獄式寫法:
sum(123, 456).then(result => {
sum(result, 7).then(result =>{
sum(result, 8).then(result => {
console.log(result)
})
})
})
//promise寫法:
sum(123, 456)
.then(result => result + 7)
.then(result => result + 8)
.then(result => console.log(result))
下來我們就解釋一下為什麼是這種鏈式呼叫
的形式:
promise中的then方法會返回一個新的Promise
,而我們接收並且輸出的是Promise的PromiseResult的值,就像這樣:then (return new Promise())
,
Promise中會儲存回撥函式的返回值,當前的引數是上一個鏈式呼叫的返回值
promise
.then(result => {
console.log("回撥函式", result)
return "會作為下個then的引數"
})
.then(result => {
console.log("第二個then", result) //第二個then,會作為下個then的引數
return "taotao真快樂"
})
.then(result => {
console.log(result) //taotao真快樂
})
promise中的
- then (return new Promise())
- catch
- 這三個方法都會返回一個新的Promise, Promise中會儲存回撥函式的返回值
- finally
- finally的返回值,不會儲存到新的Promise中
- 對Promise進行鏈式呼叫時,後面的方法(then和catch)讀取的上一步的執行結果
- 如果上一步執行結果不是當前想要的結果,則跳過當前的方法,執行下一個方法
- 一般都把catch寫到最後,只寫一個,最後統一處理異常
- 當Promise出現異常時,而整個呼叫鏈中沒有catch,則異常會向外丟擲
3.2Promise靜態方法
-
Promise.resolve()
建立一個立即完成的Promise-
Promise.resolve('成功呼叫時的資料').then( (result)=>{ console.log(1111) }) //相當於 new Promise((resolve, reject)=>{ resolve('成功呼叫時的資料') }).then(result=>{ console.log(1111) })
-
-
Promise.reject()
建立一個立即拒絕的Promise-
Promise.reject('錯誤')
-
-
Promise.all([...])
同時返回多個Promise的執行結果-
其中有一個報錯,就返回錯誤
-
function sum(a, b){ return new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve(a + b) },1000) }) } //傳遞一個可迭代物件(類陣列) Promise.all( sum(111, 222), Promise.reject("哈哈"), sum(222, 333), sum(333, 444) ).then((result)=>{ console.log(result) //[ 579, 11, 77 ] }).catch ((reason)=>{ console.log(reason) //'哈哈' })
-
-
Promise.allSettled([...])
同時返回多個Promise的執行結果(無論成功或失敗)-
{status: 'fulfilled', value: 579}
-
{status: 'rejected', reason: '哈哈'}
-
Promise.allSettled([ sum(123, 456), sum(5, 6), Promise.reject("哈哈"), sum(33, 44) ]).then(r => { console.log(r) }) //返回的結果如下: [ { status: 'fulfilled', value: 579 }, { status: 'fulfilled', value: 11 }, { status: 'rejected', reason: '哈哈' }, { status: 'fulfilled', value: 77 } ]
-
-
Promise.race([...])
返回執行最快的Promise(不考慮對錯)-
Promise.race([ Promise.reject(1111), sum(123, 456), sum(5, 6), sum(33, 44) ]).then(r => { console.log(r) }).catch(r=>{ console.log(r) }) //執行結果如下 1111 Promise.reject(1111),不用等定時器執行結束,直接就呼叫
-
-
Promise.any([...])
返回執行最快的完成的Promise-
Promise.any([ Promise.reject(1111), Promise.reject(2222), Promise.reject(3333), ]).then(r => { console.log(r) }).catch(r => { console.log("錯誤", r) })
-