如何终断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 并无终止,网络请求依然可能返回,只不过那时咱们已经不关心请求结果了。开发

相关文章
相关标签/搜索