Compartir tecnología

Cómo cancelar la llamada de la interfaz en el front-end

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

🧑‍💻 写在开头
点赞 + 收藏 === 学会🤣🤣🤣

1. ¿Cómo cancela xmlHttpRequest la solicitud?

También hay un método de cancelación en el objeto XMLHttpRequest instanciado.

const xhr = new XMLHttpRequest();
xhr.addEventListener('load', function(e) {
  console.log(this.responseText);
});
xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1');
xhr.send();

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
// 返回
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Si se cancela directamente después del envío, cancelar

// xhr的取消操作:执行过程比较模糊,不知道abort什么时机进行处理
xhr.abort()

  • 1
  • 2
  • 3

Si cancela la solicitud durante el temporizador (cuando la duración del temporizador es similar a la duración de la solicitud de la interfaz), encontrará que se ha obtenido el recurso, pero no hay impresión en la consola.
Insertar descripción de la imagen aquí

2. Controlador de aborto

const ac = new AbortController();
const { signal } = ac;
const url = "https://jsonplaceholder.typicode.com/todos/1";
​
fetch(url, { signal })
  .then((res) => res.json())
  .then((json) => console.log(json));

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Utilice cancelar directamente para cancelar la solicitud.

ac.abort()

  • 1
  • 2

Insertar descripción de la imagen aquí
El motivo del error informado aquí es que el error no se capturó.

// 修改后的代码
fetch(url, { signal: ac.signal })
  .then((res) => res.json())
  .then((json) => console.log(json))
  .catch(e => console.log(e)) // DOMException: signal is aborted without reason
ac.abort() // abort接受一个入参,会被传递到signal的reason属性中

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

¿Por qué se puede cancelar así?

fetch supervisa el estado del objeto de señal y puede finalizar la solicitud

2.1 ¿Cómo cancelar múltiples solicitudes al mismo tiempo?

const ac = new AbortController();
const { signal } = ac;
const url = "https://jsonplaceholder.typicode.com/todos";
​
const todoRequest = (id, { signal }) => {
  fetch(`${url}/${id}`, { signal })
    .then((res) => res.json())
    .then((json) => console.log(json))
    .catch((e) => console.log(e)); // DOMException: signal is aborted without reason
};
​
todoRequest(1, { signal });
todoRequest(2, { signal });
todoRequest(3, { signal });
​
ac.abort("cancled");

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

Insertar descripción de la imagen aquí

2.2 Señal de aborto

es una interfaz utilizada para representar un objeto de señal que le permite comunicarse con una operación asincrónica que se está realizando para que la operación pueda cancelarse antes de que se complete.

2.3 Método AbortSignal

2.3.1 abortar

Método estático utilizado para crear un objeto AbortSignal abortado. Cuando llama a este método, devuelve una instancia de AbortSignal con estado abortado verdadero.

const signalAbout = AbortSignal.abort(); // AbortSignal {aborted: true, reason: DOMException: signal is aborted without reason...}

  • 1
  • 2

2.3.2 método throwIfAborted

Se utiliza para comprobar si AbortSignal ha sido cancelado antes de ejecutar el código. Si AbortSignal ha sido abortado, arrojará un AbortError. Este método puede ayudar a los desarrolladores a garantizar que no se cancelen antes de realizar una operación específica para evitar procesamientos innecesarios.

const signalAbout = AbortSignal.abort('abortedReason');
try {
    signalAbout.throwIfAborted(); // 抛出error: abortedReason
} catch (error) {
    console.log(error);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.3.3 tiempo de espera

Se utiliza para crear un objeto AbortSignal que finaliza automáticamente después de un tiempo específico. Esto resulta útil cuando necesita establecer un tiempo de espera de solicitud.

// 使用 AbortSignal.timeout 设置 10ms超时
const signalAbout = AbortSignal.timeout(10);
const todoRequest = (id, { signal }) => {
  fetch(`${url}/${id}`, { signal })
    .then((res) => res.json())
    .then((json) => console.log("json: ", json))
    .catch((e) => console.log("err: ", e)); //DOMException: signal timed out 
};
todoRequest(1, { signal: signalAbout });

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Insertar descripción de la imagen aquí

2.3.3.1 Agregar detector de eventos => de no terminado a terminado

AbortSignal hereda de EventTarget, porque AbortSignal se usa para escuchar eventos de cancelación y EventTarget proporciona un mecanismo para agregar, eliminar y activar detectores de eventos.

const signalAbout = AbortSignal.timeout(10);
signalAbout.addEventListener("abort", (e) => {
    console.log("aborted: ", e);
})
​

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

La impresión de e es la siguiente:
Insertar descripción de la imagen aquí

3. Implementar una promesa cancelada de forma proactiva

const ac = new AbortController();
const { signal } = ac;
​
const cancelablePromise = ({signal}) => 
    new Promise((resolve, reject) => {
        // 情况1:直接主动取消
        signal?.throwIfAborted(); // 也可以用reject
​
        // 情况2:正常处理业务逻辑
​
        setTimeout(() => {
            Math.random() > 0.5 ? resolve('data') : reject('fetch error');
        }, 1000);
​
        // 情况3:超时 todo?
​
        // 监听取消
        signal.addEventListener("abort", () => {
            reject(signal?.reason);
        });
    })
// 发起网络请求
cancelablePromise({signal})
.then(res => console.log('res: ', res))
.catch(err => console.log('err: ', err))
// 情况1 
// ac.abort('用户离开页面了') // err:  用户离开页面了
​
// 情况2 正常请求 err:  fetch error || res:  data

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

4. ¿Cómo utilizar la señal para cancelar la escucha de eventos?

Cuando se agregan varios detectores de eventos a un elemento, no es necesario cancelarlo como removeEventListener. Cada evento debe cancelarse una vez y el identificador del evento correspondiente debe escribirse cada vez.

Cuando use la señal, solo necesita cancelar la señal una vez y se cancelará todo el monitoreo de eventos.

const ac = new AbortController();
const { signal } = ac;
const eleA = document.querySelector('#a');
const eleB = document.querySelector('#b');
​
function aEleHandler () {}; // 事件
eleA.addEventListener('click', aEleHandler, {signal}); // 无论绑定多少个事件,都只需要一个signal
​
eleB.addEventListener('click', () => {
    ac.abort(); // 只需要取消一次
})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

5. Escenarios en los que se solicitan múltiples interfaces para el ensamblaje de datos

¿Cómo cancelar esta solicitud de red continua cuando la velocidad de la red no es buena?

const ac = new AbortController();
const { signal } = ac;
const fetchAndRenderAction = (signal) => {
    requestData(signal); // 多个串行或者并行的接口
    drawAndRender(signal); // 异步渲染
}
​
try{
    fetchAndRenderAction({signal})
}catch{
    // dosomething...
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

6. Resumen

Cuando el usuario abandona activamente la página, o la red del usuario está muy atascada (el orden de retorno esperado es: interfaz 1 => interfaz 2; pero el retorno de la interfaz 1 es demasiado lento, lo que hace que el orden sea confuso). Esto requiere manual terminación de la solicitud. La señal del semáforo de instancia del constructor AbortController (se puede almacenar como referencia), la señal se utiliza como parámetro de recuperación. En cada solicitud, se puede llamar manualmente al método de aborto para cancelar la solicitud anterior.

Si le resulta útil, puede prestar atención. Actualizaré la documentación técnica periódicamente para que todos puedan discutir, aprender y progresar juntos.
Insertar descripción de la imagen aquí