Abort Requests
Keq provides two ways to abort a request:
- Direct abort (
.abort()): keep the request instance and call it at the desired moment. - Reactive abort (
.abortWhen()): declare an abort source (AbortSignal/Promise); the request terminates when that source aborts or settles.
Keq requests are lazy: request.get(...) is only sent after await, .then() or .end().
Direct Abort - .abort()
Call .end() to send the request immediately, keep the request instance and abort it at any time:
import { request, AbortException } from 'keq'
const handler = request.get('/api/cats')
// end() sends the request immediately and returns a Promise
const pending = handler.end()
// While it is in flight, application code decides when to abort
handler.abort(new AbortException('User left the page'))
try {
await pending
} catch (err) {
if (err instanceof AbortException) {
console.error('Request aborted:', err.message)
}
}When omitted, the abort reason defaults to new AbortException('Request aborted'); any value is accepted as the abort reason:
const handler = request.get('/api/cats')
handler.end()
handler.abort()
// handler.abort('user navigated away')Calling .abort() before the request is sent is supported as well: the request is never sent and the await rejects with the abort reason. Calling .abort() after the request completed has no effect.
Reactive Abort - .abortWhen()
.abortWhen() declares the abort timing as an abort source: the request terminates when the AbortSignal aborts or the Promise settles, which works well with component unmounting, route changes and other async flows.
AbortSignal
import { request, AbortException } from 'keq'
const controller = new AbortController()
try {
// The request is sent when awaited; it aborts when the controller aborts
const cats = await request
.get('/api/cats')
.abortWhen(controller.signal)
} catch (err) {
if (err instanceof AbortException) {
console.error('Request aborted:', err.message)
}
}The controller is owned by the caller (for example aborted on component unmount or when the user clicks cancel). If the signal is already aborted, the request is aborted as soon as it starts executing; the abort reason is taken from signal.reason (a default DOMException with name AbortError when no reason is passed). The listener registered by abortWhen is removed automatically once the request settles (success or failure).
Promise
import { request } from 'keq'
// Resolves when the route changes, for example
const routeChanged = new Promise<void>((resolve) => {
router.afterEach(() => resolve())
})
// The request aborts when the Promise settles (resolves or rejects)
const cats = await request
.get('/api/cats')
.abortWhen(routeChanged)Aborting in Middleware
When the abort logic should be reusable or conditional, put it into a middleware. Middlewares run while the request is being executed, so they can call context.abort(reason) at any stage to abort the current request.
The following is a minimal abort-after example (for pure timeouts, prefer the built-in .timeout()):
import { request, AbortException, KeqMiddleware } from 'keq'
function abortAfter(milliseconds: number): KeqMiddleware {
return async (context, next) => {
const timer = setTimeout(() => {
context.abort(new AbortException(`Request not finished in ${milliseconds}ms`))
}, milliseconds)
try {
await next()
} finally {
clearTimeout(timer)
}
}
}
await request
.get('/api/cats')
.use(abortAfter(3000))Difference from Timeout and Flow Control
.timeout(): aborts the request automatically when the timeout is reached and rejects with aDOMException(nameisAbortError); retries still happen when.retry()is configured..flowControl('abort'): aborts the old request automatically when a new request with the same key is sent..abort()/.abortWhen(): application code controls the abort directly or declares the abort timing; combined with middlewares it can become a global policy.