跳到主要内容

终止请求

Keq 提供两种终止请求的方式:

  • 主动终止(.abort()):持有请求实例,在业务时机直接调用。
  • 响应式终止(.abortWhen()):声明一个中止源(AbortSignal / Promise),请求会响应它的中止或完成而终止。

Keq 的请求是惰性的:request.get(...) 直到 await、.then() 或 .end() 才会真正发送。

主动终止 - .abort()​

调用 .end() 立即发送请求,并保留请求实例,在任意时机中止:

import { request, AbortException } from 'keq'

const handler = request.get('/api/cats')

// end() 立即发送请求,并返回 Promise
const pending = handler.end()

// 请求进行中,由业务代码决定何时中止
handler.abort(new AbortException('用户离开页面'))

try {
  await pending
} catch (err) {
  if (err instanceof AbortException) {
    console.error('请求已终止:', err.message)
  }
}

不传参数时,中止原因默认为 new AbortException('Request aborted');也支持任意值作为中止原因:

const handler = request.get('/api/cats')
handler.end()

handler.abort()
// handler.abort('user navigated away')

在请求发送前调用 .abort() 同样有效:请求不会发送,await 时直接以中止原因拒绝。请求完成后调用 .abort() 不再产生任何效果。

响应式终止 - .abortWhen()​

.abortWhen() 把中止时机声明为一个中止源:AbortSignal 中止、或 Promise 完成时,请求随之终止,适合与组件卸载、路由切换等异步流程联动。

AbortSignal​

import { request, AbortException } from 'keq'

const controller = new AbortController()

try {
  // await 时发送请求;controller 中止时请求随之中止
  const cats = await request
    .get('/api/cats')
    .abortWhen(controller.signal)
} catch (err) {
  if (err instanceof AbortException) {
    console.error('请求已终止:', err.message)
  }
}

controller 由调用方持有(例如组件卸载、用户点击取消时调用 controller.abort(reason))。如果传入的 signal 已经中止,请求会在开始执行时立即终止;中止原因取自 signal.reason(未传原因时为默认的 DOMException,name 为 AbortError)。请求结束(成功或失败)后,abortWhen 注册的监听会被自动移除。

Promise​

import { request } from 'keq'

// 例如路由变化时 resolve
const routeChanged = new Promise<void>((resolve) => {
  router.afterEach(() => resolve())
})

// Promise 完成(resolve 或 reject)时终止请求
const cats = await request
  .get('/api/cats')
  .abortWhen(routeChanged)

通过中间件中止​

如果希望把中止逻辑做成可复用的能力,或者按条件中止,可以放进中间件。中间件在请求执行过程中运行,因此可以在任意阶段调用 context.abort(reason) 中止当前请求。

下面是一个按时间中止的最小示例(如果是纯超时,优先使用内置的 .timeout()):

import { request, AbortException, KeqMiddleware } from 'keq'

function abortAfter(milliseconds: number): KeqMiddleware {
  return async (context, next) => {
    const timer = setTimeout(() => {
      context.abort(new AbortException(`请求超过 ${milliseconds}ms 未完成`))
    }, milliseconds)

    try {
      await next()
    } finally {
      clearTimeout(timer)
    }
  }
}

await request
  .get('/api/cats')
  .use(abortAfter(3000))

与超时、并发控制的区别​

  • .timeout():超时后自动中止请求,并以 DOMException(name 为 AbortError)拒绝;配置了 .retry() 时仍会重试。
  • .flowControl('abort'):发送同一 key 的新请求时,自动中止旧请求。
  • .abort() / .abortWhen():由业务代码主动控制或声明中止时机;配合中间件可以做成全局策略。