"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Dominando o cancelamento de promessas em JavaScript

Dominando o cancelamento de promessas em JavaScript

Publicado em 2024-11-02
Navegar:792

Escrito por Rosario De Chiara✏️

Em JavaScript, as promessas são uma ferramenta poderosa para lidar com operações assíncronas, particularmente úteis em eventos relacionados à IU. Representam um valor que pode não estar disponível imediatamente, mas será resolvido em algum momento no futuro.

As promessas permitem (ou deveriam permitir) que os desenvolvedores escrevam códigos mais limpos e gerenciáveis ​​ao lidar com tarefas como chamadas de API, interações do usuário ou animações. Ao usar métodos como .then(), .catch() e .finally(), Promises permitem uma maneira mais intuitiva de lidar com cenários de sucesso e erro, evitando o notório “inferno de retorno de chamada”.

Neste artigo, usaremos o novo método Promise.withResolvers() (março de 2024) que permite escrever um código mais limpo e simples, retornando um objeto contendo três coisas: uma nova Promise e duas funções, uma para resolver a Promise e a outra para rejeitá-la. Como esta é uma atualização recente, você precisará de um tempo de execução do Node recente (v>22) para executar os exemplos neste artigo.

Comparando os antigos e novos métodos de promessa JavaScript

Nos dois blocos de código funcionalmente equivalentes a seguir, podemos comparar a abordagem antiga e a nova abordagem de atribuir o método para resolver ou rejeitar uma promessa:

let resolve, reject;

const promise = new Promise((res, rej) => {
  resolve = res;
  reject = rej;
});

Math.random() > 0.5 ? resolve("ok") : reject("not ok");

No código acima, você pode ver o uso mais tradicional de uma Promise: você instancia um novo objeto de promessa, e então, no construtor, você tem que atribuir as duas funções, resolver e rejeitar, que serão invocadas quando necessário.

No trecho de código a seguir, o mesmo pedaço de código foi reescrito com o novo método Promise.withResolvers() e parece mais simples:

const { promise, resolve, reject } = Promise.withResolvers();

Math.random() > 0.5 ? resolve("ok") : reject("not ok");

Aqui você pode ver como funciona a nova abordagem. Ele retorna a Promessa, na qual você pode invocar o método .then() e as duas funções, resolver e rejeitar.

A abordagem tradicional para promessas encapsula a lógica de criação e tratamento de eventos em uma única função, o que pode ser limitante se várias condições ou diferentes partes do código precisarem resolver ou rejeitar a promessa.

Em contraste, Promise.withResolvers() oferece maior flexibilidade ao separar a criação da Promise da lógica de resolução, tornando-a adequada para gerenciar condições complexas ou múltiplos eventos. No entanto, para casos de uso simples, o método tradicional pode ser mais simples e mais familiar para aqueles acostumados com padrões de promessa padrão.

Exemplo do mundo real: chamando uma API

Agora podemos testar a nova abordagem em um exemplo mais realista. No código abaixo, você pode ver um exemplo simples de invocação de API:

function fetchData(url) {
    return new Promise((resolve, reject) => {
        fetch(url)
            .then(response => {
                // Check if the response is okay (status 200-299)
                if (response.ok) {
                    return response.json(); // Parse JSON if response is okay
                } else {
                    // Reject the promise if the response is not okay
                    reject(new Error('API Invocation failed'));
                }
            })
            .then(data => {
                // Resolve the promise with the data
                resolve(data);
            })
            .catch(error => {
                // Catch and reject the promise if there is a network error
                reject(error);
            });
    });
}

// Example usage
const apiURL = '';

fetchData(apiURL)
    .then(data => {
        // Handle the resolved data
        console.log('Data received:', data);
    })
    .catch(error => {
        // Handle any errors that occurred
        console.error('Error occurred:', error);
    });

A função fetchData foi projetada para pegar uma URL e retornar uma promessa que lida com uma chamada de API usando a API fetch. Ele processa a resposta verificando se o status da resposta está na faixa 200-299, indicando sucesso.

Se for bem-sucedido, a resposta será analisada como JSON e a promessa será resolvida com os dados resultantes. Se a resposta não for bem-sucedida, a promessa será rejeitada com uma mensagem de erro apropriada. Além disso, a função inclui tratamento de erros para detectar quaisquer erros de rede, rejeitando a Promessa se tal erro ocorrer.

O exemplo demonstra como usar essa função, mostrando como gerenciar os dados resolvidos com um bloco .then() e lidar com erros usando um bloco .catch(), garantindo que tanto a recuperação de dados bem-sucedida quanto os erros sejam gerenciados adequadamente.

No código abaixo, reescrevemos a função fetchData() usando o novo método Promise.withResolvers():

function fetchData(url) {
    const { promise, resolve, reject } = Promise.withResolvers();

    fetch(url)
        .then(response => {
            // Check if the response is okay (status 200-299)
            if (response.ok) {
                return response.json(); // Parse JSON if response is okay
            } else {
                // Reject the promise if the response is not okay
                reject(new Error('API Invocation failed'));
            }
        })
        .then(data => {
            // Resolve the promise with the data
            resolve(data);
        })
        .catch(error => {
            // Catch and reject the promise if there is a network error
            reject(error);
        });

    return promise;
}

Como você pode ver, o código acima é mais legível e o papel do objeto Promise é claro: a função fetchData retornará uma Promise que será resolvida com sucesso ou falhará, invocando – em cada caso – o método adequado . Você pode encontrar o código acima no repositório chamado api.invocation.{old|new}.js.

Promete cancelamento

O código a seguir explora como implementar um método de cancelamento de promessa. Como você deve saber, não é possível cancelar uma promessa em JavaScript. As promessas representam o resultado de uma operação assíncrona e são projetadas para serem resolvidas ou rejeitadas uma vez criadas, sem nenhum mecanismo integrado para cancelá-las.

Essa limitação surge porque as Promessas têm um processo de transição de estado definido; começam como pendentes e, uma vez liquidados, não podem mudar de estado. Destinam-se a encapsular o resultado de uma operação em vez de controlar a operação em si, o que significa que não podem influenciar ou cancelar o processo subjacente. Esta escolha de design mantém as promessas simples e focadas em representar o resultado final de uma operação:

const cancellablePromise = () => {
    const { promise, resolve, reject } = Promise.withResolvers();

    promise.cancel = () => {
        reject("the promise got cancelled");
    };
    return promise;
};

No código acima, você pode ver o objeto chamado cancellablePromise, que é uma promessa com um método cancel() adicional que, como você pode ver, simplesmente força a invocação do método rejeitar. Isso é apenas um açúcar sintático e não cancela uma promessa JavaScript, embora possa ajudar a escrever um código mais claro.

Uma abordagem alternativa é usar um AbortController e AbortSignal, que pode ser vinculado à operação subjacente (por exemplo, uma solicitação HTTP) para cancelá-la quando necessário. Na documentação, você pode ver que a abordagem AbortController e AbortSignal é uma implementação mais expressiva do que implementamos no código acima: uma vez que o AbortSignal é invocado, a promessa simplesmente é rejeitada.

Outra abordagem é usar bibliotecas de programação reativas como RxJS, que oferece uma implementação do padrão Observable, um controle mais sofisticado sobre fluxos de dados assíncronos, incluindo recursos de cancelamento.

Uma comparação entre observáveis ​​e promessas

Ao falar sobre casos de uso práticos, as Promises são adequadas para lidar com operações assíncronas únicas, como a busca de dados de uma API. Por outro lado, os Observáveis ​​são ideais para gerenciar fluxos de dados, como entrada do usuário, eventos WebSocket ou respostas HTTP, onde vários valores podem ser emitidos ao longo do tempo.

Já esclarecemos que, uma vez iniciadas, as Promessas não podem ser canceladas, enquanto as Observáveis permitem o cancelamento cancelando a inscrição no stream. A ideia geral é que, com Observáveis, você tenha uma estrutura explícita da possível interação com o objeto:

  • Você cria um Observável e então todos os Observáveis ​​podem assiná-lo
  • O Observável realiza seu trabalho, mudando de estado e emitindo eventos. Todos os Observadores receberão as atualizações – esse é o principal diferencial do Promises. Uma Promessa pode ser resolvida apenas uma vez enquanto os Observáveis ​​podem continuar emitindo eventos enquanto houver Observadores
  • Uma vez que o Observador não esteja interessado nos eventos dos Observáveis, ele pode cancelar a assinatura, liberando recursos

Isso é demonstrado no código abaixo:

import { Observable } from 'rxjs';

const observable = new Observable(subscriber => {
  subscriber.next(1);
  subscriber.next(2);
  subscriber.next(3);
  subscriber.complete();
});

const observer = observable.subscribe({
  next(x) { console.log('Received value:', x); },
  complete() { console.log('Observable completed'); }
});

observer.unsubscribe();

Este código não pode ser reescrito com Promises porque o Observable retorna três valores enquanto uma Promise só pode ser resolvida uma vez.

Para experimentar ainda mais o método unsubscribe, podemos adicionar outro Observer que usará o método takeWhile(): ele permitirá que o Observer espere que os valores correspondam a uma condição específica; no código abaixo, por exemplo, ele continua recebendo eventos do Observable enquanto o valor não for 2:

import { Observable, takeWhile } from 'rxjs';

const observable = new Observable(subscriber => {
  subscriber.next(1);
  subscriber.next(2);
  subscriber.next(3);
  subscriber.complete();
});

const observer1 = observable.subscribe({
  next(x) { console.log('Received by 1 value:', x); },
  complete() { console.log('Observable 1 completed'); }
});

const observer2 = observable.pipe(
  takeWhile(value => value != "2")
).subscribe(value => console.log('Received by 2 value:', value));

No código acima, observer1 é o mesmo que já vimos: ele apenas se inscreverá e continuará recebendo todos os eventos do Observable. O segundo, observer2, receberá elementos do Observable enquanto a condição for atendida. Neste caso, significa quando o valor é diferente de 2.

A partir da execução, você pode ver como funcionam os dois mecanismos diferentes:

$ node observable.mjs
Received by 1 value: 1
Received by 1 value: 2
Received by 1 value: 3
Observable 1 completed
Received by 2 value: 1
$

Conclusão

Neste artigo, investigamos o novo mecanismo para alocar uma promessa em JavaScript e apresentamos algumas das maneiras possíveis de cancelar uma promessa antes de sua conclusão. Também comparamos Promessas com objetos Observáveis, que não apenas oferecem os recursos das Promessas, mas também as estendem, permitindo múltiplas emissões de eventos e um mecanismo adequado para cancelamento de assinatura.


LogRocket: depure erros de JavaScript mais facilmente entendendo o contexto

Depurar código é sempre uma tarefa tediosa. Mas quanto mais você entender seus erros, mais fácil será corrigi-los.

LogRocket permite que você entenda esses erros de maneiras novas e exclusivas. Nossa solução de monitoramento de front-end rastreia o envolvimento do usuário com seus front-ends JavaScript para permitir que você veja exatamente o que o usuário fez que levou a um erro.

Mastering promise cancellation in JavaScript

LogRocket registra logs de console, tempos de carregamento de página, rastreamentos de pilha, solicitações/respostas de rede lentas com corpos de cabeçalhos, metadados do navegador e logs personalizados. Compreender o impacto do seu código JavaScript nunca será tão fácil!

Experimente gratuitamente.

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/logrocket/mastering-promise-cancellation-in-javascript-1eb7?1 Se houver alguma violação, entre em contato com [email protected] para excluí-lo
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3