AbortController en JavaScript: cancelar operaciones | Nicolás Garzón
La cancelación en JavaScript es cooperativa: una señal comunica que el trabajo ya no debe continuar y la operación debe observarla.
JavaScript
Copiar const controller = new AbortController ( ) ;
fetch ( url, {
signal : controller. signal,
} ) ;
controller. abort ( ) ;
Texto
Copiar AbortController
└─ signal → se comparte con operaciones
controller.abort(reason)
└─ cambia signal.aborted a trueEl controller emite la decisión. La señal se distribuye.
JavaScript
Copiar if ( signal. aborted) {
throw signal. reason;
} JavaScript
Copiar signal. throwIfAborted ( ) ; Esto permite fallar temprano antes de iniciar trabajo.
JavaScript
Copiar function wait ( delay, { signal } = { } ) {
return new Promise ( ( resolve, reject ) => {
signal?. throwIfAborted ( ) ;
const timeoutId = setTimeout ( resolve, delay) ;
signal?. addEventListener (
"abort" ,
( ) => {
clearTimeout ( timeoutId) ;
reject ( signal. reason) ;
} ,
{ once : true } ,
) ;
} ) ;
} Una implementación completa debe limpiar el listener también cuando el timer termina normalmente.
JavaScript
Copiar controller. abort (
new Error ( "User left the page" ) ,
) ; La razón puede ser cualquier valor, aunque un Error suele ser más diagnosticable.
Muchas APIs utilizan un DOMException con nombre AbortError cuando no se proporciona otra razón.
JavaScript
Copiar try {
await fetch ( url, { signal } ) ;
} catch ( error) {
if ( error. name === "AbortError" ) {
return ;
}
throw error;
} No clasifiques todos los errores como cancelación.
JavaScript
Copiar const signal = AbortSignal. timeout ( 5000 ) ;
const response = await fetch ( url, { signal } ) ; La señal se aborta después del tiempo indicado. Comprueba soporte del runtime objetivo.
JavaScript
Copiar const signal = AbortSignal. any ( [
userController. signal,
AbortSignal. timeout ( 5000 ) ,
] ) ; La operación se cancela cuando cualquiera se aborta.
Una señal abortada permanece abortada.
JavaScript
Copiar controller. abort ( ) ;
controller. signal. aborted; No reutilices el mismo controller para una operación posterior independiente.
JavaScript
Copiar let activeController;
async function search ( query ) {
activeController?. abort ( ) ;
activeController = new AbortController ( ) ;
const response = await fetch (
` /api/search?q= ${ encodeURIComponent ( query) } ` ,
{ signal : activeController. signal } ,
) ;
return response. json ( ) ;
} Cada búsqueda invalida el trabajo anterior.
JavaScript
Copiar async function loadDashboard ( userId, { signal } ) {
const [ profile, orders] = await Promise. all ( [
loadProfile ( userId, { signal } ) ,
loadOrders ( userId, { signal } ) ,
] ) ;
return { profile, orders } ;
} Una capa que inicia el flujo debe pasar la señal hacia operaciones descendientes.
Si una operación ya produjo efectos externos, abortar no los deshace.
JavaScript
Copiar await createPayment ( ) ;
controller. abort ( ) ; El pago puede existir. La cancelación detiene trabajo futuro cuando la API coopera; no crea transacciones.
JavaScript
Copiar await Promise. race ( [
fetch ( url) ,
timeoutPromise,
] ) ; La solicitud puede continuar aunque pierda la carrera.
Una primitiva cancelable debe retirar listeners cuando termina para evitar referencias innecesarias.
JavaScript
Copiar function operation ( { signal } ) {
function onAbort ( ) { }
signal. addEventListener ( "abort" , onAbort, {
once : true ,
} ) ;
return doWork ( ) . finally ( ( ) => {
signal. removeEventListener ( "abort" , onAbort) ;
} ) ;
} JavaScript
Copiar async function processItems ( items, { signal } ) {
const results = [ ] ;
for ( const item of items) {
signal. throwIfAborted ( ) ;
results. push ( await processItem ( item, { signal } ) ) ;
}
return results;
} La señal se comprueba entre pasos y se propaga a cada operación.
Pensar que Promise tiene cancelación propia.
Crear un timeout que deja la operación original viva.
Reutilizar una señal abortada.
No propagar signal a capas internas.
Tratar cualquier error como AbortError.
Esperar que abort revierta efectos ya confirmados.
Olvidar limpiar listeners.
Ignorar una cancelación hasta terminar un cálculo enorme.
AbortController emite cancelación; AbortSignal la comunica.
La operación debe cooperar.
Una señal abortada no vuelve a su estado inicial.
throwIfAborted permite fallar temprano.
Timeout y combinación de señales pueden modelarse con AbortSignal.
Cancelar no revierte efectos.
Promise.race no cancela por sí sola.
La señal debe propagarse por todo el flujo.
¿Por qué cancelar una solicitud después de crear un recurso no elimina automáticamente el recurso?
Respuesta Porque la cancelación solo comunica que debe detenerse trabajo pendiente. No revierte efectos externos que ya fueron aceptados por el servidor.
Iteración asíncrona explica cómo consumir valores que llegan gradualmente y requieren espera entre elementos.