skip to content
usubeni fantasy logo Usubeni Fantasy

¿Cómo se cancela Ajax? ¿Debería cancelarse?

/ 5 min read

This Post is Available In: CN EN ES JA

Cancelar Ajax

Si estás familiarizado con xhr, sabrás que Ajax se puede cancelar desde el lado del cliente utilizando XMLHttpRequest.abort(). Por supuesto, en la actualidad no es común escribir xhr a mano, excepto en entrevistas. En la ampliamente conocida biblioteca axios, hay dos formas de cancelar una solicitud:

Primero, está el antiguo cancelToken:

const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios
.get("/user/12345", {
cancelToken: source.token,
})
.catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log("Solicitud cancelada", thrown.message);
} else {
// manejar el error
}
});
axios.post(
"/user/12345",
{
name: "nuevo nombre",
},
{
cancelToken: source.token,
},
);
// cancelar la solicitud (el parámetro del mensaje es opcional)
source.cancel("Operación cancelada por el usuario.");

Luego, está la nueva (aunque no tan nueva) AbortController:

const controller = new AbortController();
axios
.get("/foo/bar", {
signal: controller.signal,
})
.then(function (response) {
//...
});
// cancelar la solicitud
controller.abort();

Una vez que el cancelToken o la signal se pasan a axios, se invocará XMLHttpRequest.abort() de alguna manera.

onCanceled = (cancel) => {
if (!request) {
return;
}
reject(!cancel || cancel.type ? new CanceledError(null, config, request) : cancel);
request.abort();
request = null;
};
config.cancelToken && config.cancelToken.subscribe(onCanceled);
if (config.signal) {
config.signal.aborted ? onCanceled() : config.signal.addEventListener("abort", onCanceled);
}

El cancelToken utiliza el patrón de publicación-suscripción para notificar a axios sobre la cancelación de la solicitud. Aunque esta parte está implementada por axios en sí, se basa en una propuesta de tc39 llamada cancelable promises proposal, que fue desechada.

Por otro lado, AbortController es una interfaz que ya se puede utilizar en los navegadores. Como su nombre lo indica, es un controlador diseñado específicamente para abortar acciones. El ejemplo que se muestra en mdn también utiliza una solicitud Ajax, pero con la moderna y popular función fetch. Esto demuestra que la implementación de axios es consistente con la de fetch.

function fetchVideo() {
controller = new AbortController(); // Crear un nuevo controlador
const signal = controller.signal;
fetch(url, { signal }) // Pasar la señal al método fetch
.then(function (response) {
console.log("Descarga completa", response);
})
.catch(function (e) {
console.log("Error de descarga: " + e.message);
});
}
abortBtn.addEventListener("click", function () {
if (controller) controller.abort(); // Llamar a controller.abort para cancelar la descarga
console.log("Descarga cancelada");
});

Otros usos de AbortController

Por supuesto, AbortController no solo se utiliza para cancelar una solicitud Ajax. Al examinar la documentación de la especificación DOM, se pueden encontrar otros dos ejemplos de uso:

Un ejemplo práctico es cancelar la escucha de eventos utilizando AbortController:

dictionary AddEventListenerOptions : EventListenerOptions {
boolean passive = false;
boolean once = false;
AbortSignal signal;
};

Al pasar signal a AddEventListener, se puede utilizar abort() para cancelar la escucha de eventos. Este método es especialmente útil para funciones de devolución de llamada anónimas.

Otro ejemplo es utilizarlo para cancelar una promesa. Este es un método más conciso y autoexplicativo… aunque en realidad, no es necesario utilizar AbortController para lograr esta funcionalidad, solo se necesita encontrar una forma de obtener el reject de la promesa. Creo que el enfoque principal de este ejemplo es aprender a utilizar onabort de la señal:

const controller = new AbortController();
const signal = controller.signal;
startSpinner();
doAmazingness({ ..., signal })
.then(result => ...)
.catch(err => {
if (err.name == 'AbortError') return;
showUserErrorMessage();
})
.then(() => stopSpinner());
// ...
controller.abort();
function doAmazingness({signal}) {
return new Promise((resolve, reject) => {
signal.throwIfAborted();
// Comenzar a hacer algo increíble y llamar a resolve(result) cuando termine.
// Pero también, estar atento a las señales:
signal.addEventListener('abort', () => {
// Detener la operación increíble y:
reject(signal.reason);
});
});
}

En resumen, la señal es simplemente un emisor simple y su funcionalidad se centra en cancelar una determinada operación. Si en algún caso no se desea implementar un objeto pubsub personalizado, esto es todo lo que se necesita.

La introducción de AbortController llega a su fin, ¿alguien ha olvidado gradualmente el título? Por último, me gustaría discutir si cancelar Ajax realmente sirve de algo.

Cancelar o no cancelar, esa es la cuestión

De hecho, esta cancelación de Ajax es solo una charla interna del frontend, el backend no sabe que se debe interrumpir, la solicitud enviada seguirá ejecutándose, a menos que el backend la maneje de manera especial, incluso si cancelas una solicitud de 10 segundos, el backend seguirá trabajando arduamente.

Entonces, ¿tiene sentido la “optimización” que vemos en algunos artículos, donde se menciona “cancelar solicitudes y mantener solo la última”?

Vamos a analizarlo según el caso. Para las solicitudes de modificación de datos como POST, incluso si la respuesta es lenta, el servidor ya está procesando la solicitud, cancelar el POST anterior y enviar uno nuevo es simplemente un comportamiento tonto.

En el caso de las solicitudes GET, y solo para algunas operaciones extremas, puede haber cierto efecto. Por ejemplo, si se intenta obtener una tabla muy larga y no se recibe respuesta, el usuario puede realizar una búsqueda rápida y obtener una pequeña cantidad de datos para mostrar, y cuando finalmente llegue la tabla larga, los datos de la búsqueda se sobrescribirán. En esta situación, la cancelación es realmente efectiva. También existe la cancelación de descargas y cargas, aunque probablemente se use muy poco.

Por último, mencionaré un beneficio que tiene sentido pero que en realidad no es muy útil: después de cancelar, se ahorra una posición de solicitud. Después de todo, los navegadores tienen un límite en la cantidad de solicitudes simultáneas para un mismo dominio. En la mayoría de los casos, el uso de un timeout es más práctico que cancelar. A menos que haya cinco o seis solicitudes extremadamente lentas en cola, el cambio de turno sigue siendo bastante rápido…

Mi recomendación personal es que esta supuesta “cancelación” es un manejo especial para casos extremadamente particulares. Es suficiente con saber que existe, no es necesario cancelar solicitudes en los interceptores sin motivo.

Referencias

评论组件加载中……