Una pantalla cerrada no equivale a una operación detenida
Al cancelar una generación, el usuario suele esperar que el trabajo se detenga. Sin embargo, la acción visible —pulsar un botón, cambiar de pantalla o dejar de esperar— solo describe lo que ocurrió en la interfaz. No demuestra por sí misma qué pasó con la petición HTTP, con el proceso del servidor ni con el procesamiento que pudiera estar realizando un proveedor externo. Son capas distintas y cada una puede tener un ciclo de vida propio.
Por eso, una integración debe especificar qué significa «cancelar» en cada límite. El navegador puede dejar de esperar una respuesta; el servidor puede cerrar una tarea que controla; y una API remota podría ofrecer, o no, un mecanismo documentado para cancelar una operación aceptada. Si una capa no propaga la señal o no conoce la operación que debería detener, el trabajo puede continuar allí aunque el usuario ya no vea su resultado.
La consecuencia práctica es importante: no muestres una cancelación como si fuera una respuesta válida y no informes que el proveedor detuvo el procesamiento sin haberlo verificado. La aplicación puede confirmar que recibió una solicitud de cancelación y que dejó de esperar localmente. Cualquier afirmación más amplia requiere evidencia del comportamiento de la integración concreta, incluida la documentación del proveedor y, cuando sea posible, una prueba observable.
Define qué se cancela antes de escribir código
Una señal de cancelación debe tener un alcance explícito. Puede afectar a la espera de la interfaz, a una conexión HTTP, a una tarea de backend o a una operación identificable en el servicio remoto. En un sistema sencillo, quizá solo controles los dos primeros niveles. En una arquitectura con un servidor intermedio, la cancelación del navegador no detiene automáticamente el trabajo de ese servidor: hace falta que la aplicación detecte la desconexión o reciba una señal explícita y la traduzca en una acción sobre la tarea correspondiente.
Conviene separar también el motivo. Una cancelación explícita ocurre cuando el usuario pide detener el trabajo. Un abandono puede ser una navegación o una desconexión sin una acción deliberada. Un timeout ocurre cuando se alcanza un plazo configurado. Los tres casos pueden acabar con la aplicación dejando de esperar, pero no significan lo mismo, no siempre deben mostrarse igual y no necesariamente permiten saber si el trabajo remoto terminó.
Define estados terminales que describan lo que la aplicación ha comprobado, no lo que supone. Por ejemplo: completado, cancelación solicitada, vencimiento local y desconexión observada. Si el sistema logra confirmar una cancelación remota mediante un mecanismo documentado, puede registrar ese resultado por separado. Mantener esos estados diferenciados mejora tanto la interfaz como el diagnóstico posterior.
Decisión según el evento observado
La tabla propone qué registrar y comunicar. No presupone que el evento local haya detenido el procesamiento remoto.
| Evento | Qué se sabe | Acción prudente |
|---|---|---|
| El usuario pulsa «Cancelar» | Se solicitó interrumpir la operación desde la interfaz. | Marcar la solicitud y propagarla a las capas que admitan cancelación. |
| El plazo local vence | La aplicación dejó de esperar dentro del plazo configurado. | Registrar timeout; no etiquetarlo como cancelación remota confirmada. |
| La vista se cierra o navega a otra pantalla | La vista dejó de estar disponible; la intención del usuario puede ser ambigua. | Limpiar la interfaz y definir explícitamente si se debe cancelar la tarea. |
| Se pierde la conexión | La comunicación se interrumpió o dejó de observarse. | Registrar la desconexión y comprobar qué proceso sigue activo. |
| Llega la respuesta al mismo tiempo que la cancelación | Hay una carrera entre dos eventos terminales. | Aplicar una regla determinista de precedencia y registrar el resultado elegido. |
Propaga la señal por los límites que controlas
En el navegador, AbortController permite crear una señal que puede asociarse a una petición realizada con fetch. Si se aborta la señal, el cliente puede interrumpir su espera por esa petición. Esto resuelve una parte del problema: el código de interfaz deja de tratar la respuesta como una operación que sigue esperando. No demuestra, por sí solo, que una tarea iniciada más allá de la conexión haya sido detenida.
El siguiente límite suele ser el backend. Si el navegador se comunica con un servicio propio, ese servicio necesita una política para asociar la petición entrante con el trabajo que inició. Según la plataforma y la biblioteca utilizadas, podrá observar una señal de cancelación, una desconexión o ambas. La aplicación debe comprobar las capacidades reales de su servidor y su cliente HTTP: no debe asumir que una abstracción propaga la cancelación solo porque la interfaz tiene un botón.
Si el backend llama a una API externa, debe verificar si su cliente HTTP admite una señal de cancelación y qué ocurre al usarla. Después debe comprobar la documentación de la API concreta para saber si ofrece un mecanismo que cancele una operación ya aceptada, o si únicamente es posible interrumpir la conexión local. Son capacidades diferentes. Una operación de cancelación explícita existe en algunas API para operaciones concretas, pero ese hecho no permite generalizar a los servicios de generación de IA.
Las guías de diseño de API asíncronas también respaldan que una operación en curso tenga una forma de cancelación. Es un principio de diseño, no una garantía de que cualquier dependencia o proveedor implemente esa capacidad, ni una receta para propagarla entre sistemas. Documenta, capa por capa, quién recibe la señal, qué acción puede realizar y qué resultado consigue observar.
Ruta de cancelación que se puede auditar
Diseña el recorrido antes de implementar la interfaz. Si una capa no tiene capacidad de cancelación, registra esa limitación en lugar de representar el recorrido como completo.
- 01La interfaz registra el motivo: acción explícita, navegación, timeout u otra desconexión observada.
- 02El cliente aborta su espera local y comunica la cancelación al backend cuando la arquitectura lo permite.
- 03El backend identifica la tarea vinculada a esa petición y solicita su interrupción si la biblioteca usada admite hacerlo.
- 04El backend verifica si la API remota ofrece una operación de cancelación documentada y la utiliza solo dentro de su alcance especificado.
- 05Cada capa registra el resultado que puede observar: solicitud recibida, conexión cerrada, tarea local detenida o cancelación remota confirmada.
Ejemplo con AbortController en el navegador
El ejemplo siguiente inicia una petición a una ruta propia de backend y permite cancelar la espera del navegador. Supone que esa ruta recibe un objeto con un campo prompt y, si termina correctamente, devuelve JSON con un campo text. La ruta es ilustrativa: el backend debe implementar el contrato y decidir por separado cómo trata la petición, cómo propaga la cancelación y qué puede confirmar sobre la API externa.
La función comprueba una señal que ya estuviera abortada antes de iniciar el envío y evita actualizar la interfaz con una respuesta si la cancelación se registra primero. En una interfaz real, asocia la función cancel al botón correspondiente y desactívalo cuando la operación alcance un estado terminal. El ejemplo no demuestra que el backend o el proveedor hayan detenido el procesamiento.
El llamador conserva el controlador para poder cancelar. Si se cancela antes de llamar a generate, la señal se detecta antes de enviar la petición.
async function generate(prompt, output, signal) {
if (signal.aborted) {
output.textContent = "Cancelación solicitada antes del envío.";
return;
}
output.textContent = "Generando…";
try {
const response = await fetch("/api/generate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prompt }),
signal
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const result = await response.json();
if (!signal.aborted) output.textContent = result.text;
} catch (error) {
if (signal.aborted || error.name === "AbortError") {
output.textContent = "Se dejó de esperar la respuesta.";
return;
}
output.textContent = "No se pudo completar la petición.";
}
}
const controller = new AbortController();
const task = generate("Resume el documento", document.querySelector("#output"), controller.signal);
// Llama a controller.abort() desde la acción de cancelar.
// Conserva task si necesitas esperar a que termine el código local.
Trata el timeout como un plazo, no como una prueba de cancelación
Un timeout define cuánto tiempo está dispuesta a esperar una capa. Puede haber un límite en la interfaz, otro en el servidor y otro en el cliente HTTP que conecta con el proveedor. Esos límites deben coordinarse de forma deliberada: si una capa abandona antes que otra sin comunicarlo, la operación puede seguir ocupando recursos en una capa que todavía la considera activa.
Cuando vence un plazo, registra qué reloj venció y en qué capa. «Timeout del navegador» es más preciso que «el modelo agotó el tiempo», salvo que exista evidencia de que el servicio remoto comunicó ese resultado. La aplicación puede dejar de esperar, solicitar la interrupción a las capas disponibles y mostrar un estado de plazo vencido. No debe convertir esa acción en una afirmación de cancelación confirmada.
Los efectos sobre el uso o la facturación dependen de la API y de sus condiciones. La documentación disponible para una API concreta puede describir qué cancela su operación; no se debe inferir de ello el comportamiento de otro endpoint ni de otro proveedor. Si ese efecto importa, localiza la documentación oficial específica, comprueba qué unidad de trabajo se cancela y valida el resultado mediante una prueba controlada. Si la documentación no lo aclara, conserva la incertidumbre en el producto y en la política de costes.
Evita estados incoherentes cuando hay carreras
La respuesta y la cancelación pueden llegar casi al mismo tiempo. Si la interfaz procesa ambos eventos sin una regla común, podría mostrar primero «cancelado» y después sobrescribirlo con una respuesta tardía. También puede ocurrir lo contrario: presentar una respuesta que llegó cuando el usuario ya había pedido detener la operación. La solución no consiste en adivinar qué evento ocurrió primero en el sistema remoto, sino en definir qué estado terminal debe prevalecer en la aplicación.
Una regla sencilla para la interfaz es hacer que la primera transición terminal registrada para una operación impida las actualizaciones posteriores de esa misma operación. Otra opción es asignar un identificador a cada petición y descartar respuestas pertenecientes a una operación que ya no es la activa. La elección debe ser coherente con el producto: si se permite conservar una respuesta completada pese a una cancelación tardía, esa política también debe ser explícita.
No mezcles la visualización con la verdad operativa. La interfaz puede marcar la operación como cancelada localmente, mientras el backend registra que la cancelación remota no se pudo confirmar. Mantén esos datos diferenciados en registros o estados internos cuando sean relevantes. Así se evita que una respuesta tardía parezca una prueba de que se deshizo la cancelación o que el cierre de una conexión parezca la finalización del trabajo.
Prueba cada fase y cada límite
Una prueba que solo pulsa cancelar después de ver una respuesta no comprueba la cancelación durante el trabajo. Diseña casos separados para los momentos relevantes y observa tanto la interfaz como el backend. Si hay una API externa, comprueba únicamente lo que la integración y la documentación permiten observar; no uses la desaparición del resultado en pantalla como indicador de que el proveedor dejó de procesar.
Incluye un caso de cancelación antes del envío: la operación no debería iniciar la petición si la señal ya está cancelada al entrar en la función. Prueba también la cancelación después de iniciar la petición pero antes de recibir una respuesta, y durante una respuesta en curso si el endpoint utiliza una modalidad que permita observar ese intervalo. Comprueba el comportamiento al cerrar o abandonar la vista según la política que haya definido el producto.
Finalmente, introduce una carrera entre respuesta y cancelación y verifica que el estado terminal sea determinista. Para un timeout, comprueba que se registre como plazo vencido, no como una acción explícita del usuario. En cada prueba, revisa si quedan tareas y conexiones locales abiertas y si el backend mantiene trabajo activo. Si no es posible inspeccionar una actividad remota, anótalo como una limitación en vez de afirmar que se detuvo.
Matriz mínima de pruebas
Adapta los observables a la arquitectura. La columna final describe una comprobación local o documental, no una garantía universal del proveedor.
| Escenario | Comprobación en la aplicación | Límite que debe documentarse |
|---|---|---|
| Señal cancelada antes de llamar a la función | No se inicia la petición; la interfaz conserva un estado coherente. | La función y sus llamadores respetan la señal antes del envío. |
| Cancelación durante la espera | La interfaz deja de esperar y no trata el aborto como respuesta válida. | Qué puede observar y detener el backend. |
| Abandono de la vista | Se aplica la política definida y no se actualiza una vista que ya no corresponde. | Si el backend recibe alguna señal o conserva la tarea. |
| Timeout | Se registra como vencimiento del plazo en la capa correcta. | Si la petición o el trabajo remoto pueden seguir activos. |
| Respuesta simultánea a la cancelación | La regla de precedencia produce un único estado terminal visible. | Qué resultado registra cada capa, sin inferir causalidad remota. |
| Cierre o pérdida de conexión | Se detecta la desconexión cuando la plataforma lo permite y se liberan recursos locales. | Si la desconexión se propaga a la tarea backend y a la API externa. |
Verifica recursos y consecuencias antes del despliegue
La verificación debe recorrer toda la cadena, no solo el código del botón. Confirma que el cliente HTTP local pueda recibir una señal de cancelación, que el backend relacione esa señal con la tarea correcta y que el mecanismo remoto —si existe— corresponda al endpoint y al tipo de operación utilizados. Las capacidades de cancelación específicas de un producto no prueban que otra API del mismo proveedor, u otro proveedor, tenga un comportamiento equivalente.
Observa también la limpieza local: tareas auxiliares, listeners, conexiones y referencias asociadas a una petición no deben quedar activas sin una razón conocida. En servicios con varias solicitudes simultáneas, asegúrate de que una señal cancele solo la operación asociada y no una tarea compartida por otros usuarios. Registra identificadores de operación y estados finales de forma que sea posible reconstruir qué se solicitó y qué se confirmó.
Antes de afirmar algo sobre costes, uso o procesamiento remoto, consulta la documentación oficial de esa integración y realiza pruebas limitadas que concuerden con sus términos. Una API de llamadas por lotes, por ejemplo, documenta una operación de cancelación para sus llamadas y destinatarios; es evidencia sobre ese endpoint, no sobre la generación de texto. Mantén esa distinción en el diseño, las pruebas y la comunicación al usuario.
Lista de aceptación
No despliegues una promesa de cancelación más amplia que el comportamiento que puedas comprobar.
- 01La interfaz diferencia cancelación solicitada, timeout y desconexión.
- 02La señal llega a cada capa que efectivamente admite cancelación, y las capas que no la admiten están documentadas.
- 03El backend puede asociar una señal con la operación correcta y registrar el resultado observado.
- 04El cliente de la API externa y la propia API tienen capacidades verificadas en su documentación específica.
- 05Las tareas, conexiones y estados locales se revisan tras cancelar, completar, agotar el plazo y perder la conexión.
- 06Las pruebas cubren cancelación previa al envío, durante la espera y en una carrera con la respuesta.
- 07La interfaz no afirma que el proveedor detuvo el trabajo ni que cambió la facturación sin evidencia específica.
Criterio final: informa lo que sabes y verifica lo que no
El patrón esencial es sencillo, aunque su implementación dependa de la arquitectura: registra el motivo, propaga la señal por las capas que controlas, aplica una política determinista de estados y comprueba qué recursos quedan activos. AbortController puede ayudar a interrumpir la espera en el navegador; no sustituye la cancelación en el backend ni el mecanismo remoto que pueda ofrecer una API.
Antes de integrar un servicio, responde por escrito estas preguntas: ¿la biblioteca HTTP admite cancelación?, ¿el backend detecta el abandono?, ¿la API externa tiene una operación documentada para detener una tarea aceptada?, ¿qué se observa después de usarla?, ¿qué se conoce sobre uso y facturación? Si alguna respuesta falta, registra la incertidumbre y evita prometer más de lo que la integración demuestra.
Cancelar correctamente no significa garantizar que todo trabajo desaparece en todas las capas. Significa tener estados claros, interrumpir lo que la aplicación puede interrumpir, liberar recursos locales, probar las carreras y comunicar honestamente el alcance de cada acción.
Qué sigue abierto
- Las fuentes verificadas aportadas no documentan el comportamiento de una API concreta de generación de texto ante el cierre de una conexión, una señal abortada o una solicitud de cancelación.
- No se puede generalizar a partir de los ejemplos de cancelación de llamadas por lotes o de devolución a llamadas el comportamiento de APIs de generación de IA.
- Las fuentes no establecen si una cancelación local detiene el procesamiento del proveedor ni cómo afecta al uso o a la facturación; debe comprobarse para cada integración.
- El soporte y el resultado de la cancelación en bibliotecas HTTP de backend dependen de las implementaciones elegidas y no quedan especificados en las fuentes aportadas.
Continúa explorando
Fuentes consultadas
Correcciones y transparencia
Si detectas un dato incorrecto o desactualizado, puedes enviarnos una corrección indicando la página y la fuente que debemos revisar.
Proponer una corrección