Compartilhamento de tecnologia

Como cancelar a chamada de interface no front-end

2024-07-12

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

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

1. Como o xmlHttpRequest cancela a solicitação?

Há também um método abort no 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

Se abortado logo após o envio, cancele

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

  • 1
  • 2
  • 3

Se você cancelar a solicitação durante o cronômetro (quando a duração do cronômetro for semelhante à duração da solicitação da interface), você descobrirá que o recurso foi obtido, mas não há impressão no console.
Insira a descrição da imagem aqui

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

Use abortar diretamente para cancelar a solicitação

ac.abort()

  • 1
  • 2

Insira a descrição da imagem aqui
O motivo do erro relatado aqui é que o erro não foi capturado.

// 修改后的代码
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 que pode ser cancelado assim?

fetch monitora o status do objeto de sinal e pode encerrar a solicitação

2.1 Como cancelar várias solicitações ao mesmo tempo?

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

Insira a descrição da imagem aqui

2.2 Abortar sinal

é uma interface usada para representar um objeto de sinal que permite a comunicação com uma operação assíncrona que está sendo executada para que a operação possa ser abortada antes de ser concluída.

2.3 Método AbortSignal

2.3.1 abortar

Método estático usado para criar um objeto AbortSignal abortado. Quando você chama esse método, ele retorna uma instância AbortSignal com status abortado verdadeiro.

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

  • 1
  • 2

2.3.2 método throwIfAborted

Usado para verificar se o AbortSignal foi abortado antes de executar o código. Se AbortSignal foi abortado, lançará um AbortError. Este método pode ajudar os desenvolvedores a garantir que não sejam abortados antes de executar uma operação específica para evitar processamento desnecessário.

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 tempo limite

Usado para criar um objeto AbortSignal que termina automaticamente após um tempo especificado. Isto é útil quando você precisa definir um tempo limite de solicitação.

// 使用 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

Insira a descrição da imagem aqui

2.3.3.1 Adicionar ouvinte de evento => de não encerrado para encerrado

AbortSignal herda de EventTarget, porque AbortSignal é usado para escutar eventos de anulação e EventTarget fornece um mecanismo para adicionar, remover e acionar ouvintes de eventos.

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

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

A impressão de e é a seguinte:
Insira a descrição da imagem aqui

3. Implemente uma promessa cancelada proativamente

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. Como usar o sinal para cancelar a escuta de eventos?

Quando vários ouvintes de eventos são adicionados a um elemento, ele não precisa ser cancelado como removeEventListener. Cada evento precisa ser cancelado uma vez e o identificador do evento correspondente deve ser gravado a cada vez.

Ao usar o sinal, você só precisa cancelar o sinal uma vez e todo o monitoramento de eventos será cancelado.

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. Cenários onde múltiplas interfaces são solicitadas para montagem de dados

Como cancelar esta solicitação contínua de rede quando a velocidade da rede não é boa?

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. Resumo

Quando o usuário sai ativamente da página ou a rede do usuário está muito travada (a ordem de retorno esperada é: interface 1 => interface 2; mas o retorno da interface 1 é muito lento, fazendo com que o pedido fique confuso). encerramento do pedido. O sinal do semáforo de instância do construtor AbortController (pode ser armazenado como ref), o sinal é usado como parâmetro de busca. A cada solicitação, o método abort pode ser chamado manualmente para cancelar a solicitação anterior.

Se for útil para você, fique atento. Atualizarei a documentação técnica regularmente para que todos possam discutir, aprender e progredir juntos.
Insira a descrição da imagem aqui