【JS Utils】创建手动操作的 Promise (可跳过或中断)

createManualPromise(创建手动操作的 Promise)


源码

/**
 *  创建手动操作的 Promise (可跳过或中断)
 *  @param  {Function|Promise}  executor          处理方法 ( Function | Promise: 仅pending状态可手动操作) )
 *  @param  {Object}            [options]         配置项
 *  @param  {Function}          [options.abort]   中断方法
 *  @param  {AbortSignal}       [options.signal]  中断信号
 *  @return {Promise}                             Promise: { then, catch, finally, next: 跳过方法, abort: 中断方法, aborted: 是否已中断, abortedReason: 中断原因, throwIfAborted: 中断时抛出异常方法 }
 */
function createManualPromise(executor, options) {
  let { abort, signal = {} } = options || {}
  let aborted = signal.aborted || false
  let resolve, reject, properties = {
    then: { value() { return manual(Promise.prototype.then.apply(this, arguments)) } },
    catch: { value() { return manual(Promise.prototype.catch.apply(this, arguments)) } },
    finally: { value() { return manual(Promise.prototype.finally.apply(this, arguments)) } },
    next: { value(result) { aborted || resolve(result) } },
    abort: {
      value(reason) {
        if (aborted) return
        if (reason === void 0) reason = new DOMException('Promise aborted', 'AbortError')
        aborted = true, reject(reason), signal.reason = reason
        if (!signal.aborted) signal.aborted = true, abort && options.abort(reason)
      }
    },
    aborted: { get() { return aborted } },
    abortedReason: { get() { return signal.reason } },
    throwIfAborted: { value() { if (aborted) throw signal.reason } }
  }
  let manual = (target) => Object.defineProperties(target, properties)
  let promise = typeof executor == 'function' ? new Promise(executor) : executor
  promise = manual(Promise.race([promise, new Promise((a, b) => { resolve = a, reject = b })]))
  aborted
    ? (aborted = false, promise.abort(signal.reason))
    : signal.addEventListener && signal.addEventListener('abort', () => promise.abort(signal.reason))
  return promise
}

使用示例

// 模拟一个请求接口 (5秒后返回结果)
async function request(params) {
  await new Promise(r => setTimeout(r, 5000))
  if (Math.random() > 0.5) return 'success'
  else throw 'fail'
}

// 业务场景1 (4秒后超时)
function scene1() {
  const promise = createManualPromise((resolve, reject) => {
    request({ name: 'A' }).then(resolve, reject)
    setTimeout(() => promise.abort('A: timeout'), 4000)
  }).then(console.log, console.error)
  return promise
}

// 业务场景2 (3秒后自动中断)
function scene2() {
  const promise = createManualPromise(
    request({ name: 'B' })
  ).then(console.log, console.error)
  setTimeout(() => promise.abort('B: auto abort'), 3000)
  return promise
}

// 业务场景3 (用户手动中断)
function scene3() {
  const controller = new AbortController()
  const promise = createManualPromise(
    request({ name: 'C' }), { signal: controller.signal }
  ).then(console.log, console.error)
  const button = document.createElement('button')
  button.innerText = 'Abort'
  button.onclick = () => controller.abort('C: user abort')
  document.body.appendChild(button)
  promise.then(() => button.remove())
  return promise
}

// 业务场景4 (2秒后返回自定义结果)
function scene4() {
  const promise = createManualPromise(
    request({ name: 'D' })
  ).then(console.log, console.error)
  setTimeout(() => promise.next('D: auto result'), 2000)
  return promise
}

// 自动化测试
;(async () => {
  await new Promise(r => setTimeout(r))
  console.log('===== scene1 start =====')
  await scene1()
  console.log('===== scene1 end =====')
  
  console.log('===== scene2 start =====')
  await scene2()
  console.log('===== scene2 end =====')
  
  console.log('===== scene3 start =====')
  await scene3()
  console.log('===== scene3 end =====')
  
  console.log('===== scene4 start =====')
  await scene4()
  console.log('===== scene4 end =====')
})()
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值