promise和async
阿新 • • 發佈:2021-06-29
promise
-
Promise,我們瞭解到promise是ES6為解決非同步回撥而生,避免出現這種回撥地獄,那麼為何又需要Async/Await呢?你是不是和我一樣對Async/Await感興趣以及想知道如何使用,下面一起來看看這篇文章:Async/Await替代Promise的6個理由。
-
promise的核心原理其實就是釋出訂閱模式,通過兩個佇列來快取成功的回撥(onResolve)和失敗的回撥(onReject)。如果還不熟悉promise用法的朋友,請參考Es6入門之promise物件,下面來分析promise的特點。
promise基礎版實現
new Promise時需要傳遞一個executor執行器,執行器會立刻執行;執行器中傳遞了兩個引數:resolve成功的函式、reject失敗的函式,他們呼叫時可以接受任何值的引數value
function Promise (executor){
function resolve(value){}
function reject(value){}
try{
executor(resolve,reject);
}catch(e){
reject(e);
}
}
var promise = new Promise((resolve,reject)=>{
console.log('start');
})
promise完整程式碼
// 判斷變數否為function const isFunction = variable => typeof variable === 'function' // 定義Promise的三種狀態常量 const PENDING = 'PENDING' const FULFILLED = 'FULFILLED' const REJECTED = 'REJECTED' class MyPromise { constructor (handle) { if (!isFunction(handle)) { throw new Error('MyPromise must accept a function as a parameter') } // 新增狀態 this._status = PENDING // 新增狀態 this._value = undefined // 新增成功回撥函式佇列 this._fulfilledQueues = [] // 新增失敗回撥函式佇列 this._rejectedQueues = [] // 執行handle try { handle(this._resolve.bind(this), this._reject.bind(this)) } catch (err) { this._reject(err) } } // 新增resovle時執行的函式 _resolve (val) { const run = () => { if (this._status !== PENDING) return this._status = FULFILLED // 依次執行成功佇列中的函式,並清空佇列 const runFulfilled = (value) => { let cb; while (cb = this._fulfilledQueues.shift()) { cb(value) } } // 依次執行失敗佇列中的函式,並清空佇列 const runRejected = (error) => { let cb; while (cb = this._rejectedQueues.shift()) { cb(error) } } /* 如果resolve的引數為Promise物件,則必須等待該Promise物件狀態改變後, 當前Promsie的狀態才會改變,且狀態取決於引數Promsie物件的狀態 */ if (val instanceof MyPromise) { val.then(value => { this._value = value runFulfilled(value) }, err => { this._value = err runRejected(err) }) } else { this._value = val runFulfilled(val) } } // 為了支援同步的Promise,這裡採用非同步呼叫 setTimeout(run, 0) } // 新增reject時執行的函式 _reject (err) { if (this._status !== PENDING) return // 依次執行失敗佇列中的函式,並清空佇列 const run = () => { this._status = REJECTED this._value = err let cb; while (cb = this._rejectedQueues.shift()) { cb(err) } } // 為了支援同步的Promise,這裡採用非同步呼叫 setTimeout(run, 0) } // 新增then方法 then (onFulfilled, onRejected) { const { _value, _status } = this // 返回一個新的Promise物件 return new MyPromise((onFulfilledNext, onRejectedNext) => { // 封裝一個成功時執行的函式 let fulfilled = value => { try { if (!isFunction(onFulfilled)) { onFulfilledNext(value) } else { let res = onFulfilled(value); if (res instanceof MyPromise) { // 如果當前回撥函式返回MyPromise物件,必須等待其狀態改變後在執行下一個回撥 res.then(onFulfilledNext, onRejectedNext) } else { //否則會將返回結果直接作為引數,傳入下一個then的回撥函式,並立即執行下一個then的回撥函式 onFulfilledNext(res) } } } catch (err) { // 如果函式執行出錯,新的Promise物件的狀態為失敗 onRejectedNext(err) } } // 封裝一個失敗時執行的函式 let rejected = error => { try { if (!isFunction(onRejected)) { onRejectedNext(error) } else { let res = onRejected(error); if (res instanceof MyPromise) { // 如果當前回撥函式返回MyPromise物件,必須等待其狀態改變後在執行下一個回撥 res.then(onFulfilledNext, onRejectedNext) } else { //否則會將返回結果直接作為引數,傳入下一個then的回撥函式,並立即執行下一個then的回撥函式 onFulfilledNext(res) } } } catch (err) { // 如果函式執行出錯,新的Promise物件的狀態為失敗 onRejectedNext(err) } } switch (_status) { // 當狀態為pending時,將then方法回撥函式加入執行佇列等待執行 case PENDING: this._fulfilledQueues.push(fulfilled) this._rejectedQueues.push(rejected) break // 當狀態已經改變時,立即執行對應的回撥函式 case FULFILLED: fulfilled(_value) break case REJECTED: rejected(_value) break } }) } // 新增catch方法 catch (onRejected) { return this.then(undefined, onRejected) } // 新增靜態resolve方法 static resolve (value) { // 如果引數是MyPromise例項,直接返回這個例項 if (value instanceof MyPromise) return value return new MyPromise(resolve => resolve(value)) } // 新增靜態reject方法 static reject (value) { return new MyPromise((resolve ,reject) => reject(value)) } // 新增靜態all方法 static all (list) { return new MyPromise((resolve, reject) => { /** * 返回值的集合 */ let values = [] let count = 0 for (let [i, p] of list.entries()) { // 陣列引數如果不是MyPromise例項,先呼叫MyPromise.resolve this.resolve(p).then(res => { values[i] = res count++ // 所有狀態都變成fulfilled時返回的MyPromise狀態就變成fulfilled if (count === list.length) resolve(values) }, err => { // 有一個被rejected時返回的MyPromise狀態就變成rejected reject(err) }) } }) } // 新增靜態race方法 static race (list) { return new MyPromise((resolve, reject) => { for (let p of list) { // 只要有一個例項率先改變狀態,新的MyPromise的狀態就跟著改變 this.resolve(p).then(res => { resolve(res) }, err => { reject(err) }) } }) } finally (cb) { return this.then( value => MyPromise.resolve(cb()).then(() => value), reason => MyPromise.resolve(cb()).then(() => { throw reason }) ); } }
什麼是Async/Await
- async/await是寫非同步程式碼的新方式,使用的方式看起來像同步,以前的方法有回撥函式和Promise。
- async/await是基於Promise實現的,它不能用於普通的回撥函式。
Async/Await語法
使用Promise是這樣的:
const makeRequest = () =>
getJSON()
.then(data => {
console.log(data)
return "done"
})
makeRequest()
- 示例中,getJSON函式返回一個promise,這個promise成功resolve時會返回一個JSON物件。我們只是呼叫這個函式,列印返回的JSON物件,然後返回”done”。
使用Async/Await是這樣的:
const makeRequest = async () => {
console.log(await getJSON())
return "done"
}
makeRequest()
它們有一些細微不同:
- 函式前面多了一個aync關鍵字。await關鍵字只能用在aync定義的函式內。async函式會隱式地返回一個promise,該promise的reosolve值就是函式return的值。(示例中reosolve值就是字串”done”)
- 第1點暗示我們不能在最外層程式碼中使用await,因為不在async函式內。
為什麼Async/Await更好?
1. 簡潔
- 由示例可知,使用Async/Await明顯節約了不少程式碼。我們不需要寫.then,不需要寫匿名函式處理Promise的resolve值,也不需要定義多餘的data變數,還避免了巢狀程式碼。這些小的優點會迅速累計起來,這在之後的程式碼示例中會更加明顯。
2.錯誤處理
Async/Await讓try/catch可以同時處理同步和非同步錯誤。在下面的promise示例中,try/catch不能處理JSON.parse的錯誤,因為它在Promise中。我們需要使用.catch,這樣錯誤處理程式碼非常冗餘。並且,在我們的實際生產程式碼會更加複雜。
const makeRequest = () => {
try {
getJSON()
.then(result => {
// JSON.parse可能會出錯
const data = JSON.parse(result)
console.log(data)
})
// 取消註釋,處理非同步程式碼的錯誤
// .catch((err) => {
// console.log(err)
// })
} catch (err) {
console.log(err)
}
}
- 使用aync/await的話,catch能處理JSON.parse錯誤:
const makeRequest = async () => {
try {
// this parse may fail
const data = JSON.parse(await getJSON())
console.log(data)
} catch (err) {
console.log(err)
}
}
3.條件語句
- 下面示例中,需要獲取資料,然後根據返回資料決定是直接返回,還是繼續獲取更多的資料。
// promise
const makeRequest = () => {
return getJSON()
.then(data => {
if (data.needsAnotherRequest) {
return makeAnotherRequest(data)
.then(moreData => {
console.log(moreData)
return moreData
})
} else {
console.log(data)
return data
}
})
}
- 上面的程式碼使用async/await編寫可以大大地提高可讀性:
const makeRequest = async () => {
const data = await getJSON()
if (data.needsAnotherRequest) {
const moreData = await makeAnotherRequest(data);
console.log(moreData)
return moreData
} else {
console.log(data)
return data
}
}
4. 除錯
promise
- promise不能在返回表示式的箭頭函式中設定斷點
- 如果你在.then程式碼塊中設定斷點,使用Step Over快捷鍵,偵錯程式不會跳到下一個.then,因為它只會跳過非同步程式碼
async
- async/await能夠使得程式碼除錯更簡單
- 使用await/async時,你不再需要那麼多箭頭函式,這樣你就可以像除錯同步程式碼一樣跳過await語句
- 如果它等到的不是一個 Promise 物件,那 await 表示式的運算結果就是它等到的東西。
- 如果它等到的是一個 Promise 物件,await 就忙起來了,它會阻塞後面的程式碼,等著 Promise 物件 resolve,然後得到 resolve 的值,作為 await 表示式的運算結果。