如何終斷Promise?

Promise 有個缺點就是一旦建立就沒法取消,因此本質上 Promise 是沒法被終止的,但咱們在開發過程當中可能會遇到下面兩個需求:promise

中斷調用鏈

就是在某個 then/catch 執行以後,不想讓後續的鏈式調用繼續執行了,即:網絡

somePromise
  .then(() => {})
  .then(() => {
    // 終止 Promise 鏈,讓下面的 then、catch 和 finally 都不執行
  })
  .then(() => console.log('then'))
  .catch(() => console.log('catch'))
  .finally(() => console.log('finally'))
複製代碼

答案就是在 then/catch 的最後一行返回一個永遠 pending 的 promise 便可:app

return new Promise((resolve, reject) => {})
複製代碼

這樣的話後面全部的 then、catch 和 finally 都不會執行了。dom

中斷Promise

注意這裏是中斷而不是終止,由於 Promise 沒法終止,這個中斷的意思是:在合適的時候,把 pending 狀態的 promise 給 reject 掉。例如一個常見的應用場景就是但願給網絡請求設置超時時間,一旦超時就就中斷,咱們這裏用定時器模擬一個網絡請求,隨機 3 秒以內返回:函數

const request = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('收到服務端數據')
  }, Math.random() * 3000)
})

複製代碼

若是認爲超過 2 秒就是網絡超時,能夠對該 promise 寫一個包裝函數 timeoutWrapper:ui

function timeoutWrapper(p, timeout = 2000) {
  const wait = new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('請求超時')
    }, timeout)
  })
  return Promise.race([p, wait])
}
複製代碼

因而就能夠像下面這樣用了:spa

const req = timeoutWrapper(request)
req.then(res => console.log(res)).catch(e => console.log(e))
複製代碼

不過這種方式並不靈活,由於終止 promise 的緣由可能有不少,例如當用戶點擊某個按鈕或者出現其餘事件時手動終止。因此應該寫一個包裝函數,提供 abort 方法,讓使用者本身決定什麼時候終止:code

function abortWrapper(p1) {
  let abort
  let p2 = new Promise((resolve, reject) => (abort = reject))
  let p = Promise.race([p1, p2])
  p.abort = abort
  return p
}
複製代碼

使用方法以下:事件

const req = abortWrapper(request)
req.then(res => console.log(res)).catch(e => console.log(e))
setTimeout(() => req.abort('用戶手動終止請求'), 2000) // 這裏能夠是用戶主動點擊
複製代碼

最後,再次強調一下,雖然 promise 被中斷了,可是 promise 並無終止,網絡請求依然可能返回,只不過那時咱們已經不關心請求結果了。開發

相關文章
相關標籤/搜索