React: Obsoleto `isMounted`

Criado em 14 nov. 2015  ·  48Comentários  ·  Fonte: facebook/react

isMounted já não está disponível nas classes ES6, e já temos um aviso dizendo que "podemos" removê-las, mas não temos um problema de github para descontinuá-las. De acordo com nossas discussões de hoje, basicamente concordamos que começaremos a nos afastar de isMounted e descontinuá-lo. Ainda precisamos descobrir algumas boas histórias sobre promessas (e casos de uso relacionados).

Este problema é monitorar o progresso em direção a essa meta.

Para obter informações básicas, leia:

Comentários muito úteis

Este método simples pode ser usado para adicionar cancel a qualquer promessa

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then((val) =>
      hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    );
    promise.catch((error) =>
      hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

EDITAR: Atualizado para correção / integridade.

COMO USAR

const somePromise = new Promise(r => setTimeout(r, 1000));

const cancelable = makeCancelable(somePromise);

cancelable
  .promise
  .then(() => console.log('resolved'))
  .catch(({isCanceled, ...error}) => console.log('isCanceled', isCanceled));

// Cancel promise
cancelable.cancel();

Todos 48 comentários

Eu não concordo com isso. As promessas do ES6 em particular não podem ser canceladas de forma confiável em componentWillUnmount , portanto, remover a única maneira de verificar se o componente está montado antes de setState ou outra ação está abrindo caminho para muitos bugs assíncronos difíceis de rastrear.

@yaycmyk Assim a linha:

Ainda precisamos descobrir algumas boas histórias sobre promessas (e casos de uso relacionados).

Leia os problemas de fundo que listei, em particular: https://github.com/facebook/react/issues/2787#issuecomment -68738793

Eu li os comentários. Eu apenas acho os problemas intratáveis.

Por que as promessas não podem ser canceladas de forma confiável? Quaisquer fontes / provas / exemplos?

Na segunda-feira, 16 de novembro de 2015, Evan Jacobs [email protected] escreveu:

Eu não concordo com isso. As promessas ES6 em particular não podem ser confiáveis
cancelado em componentWillUnmount, removendo assim a única maneira de verificar se
o componente é montado antes de setState ou outra ação abrir o
caminho para muitos bugs assíncronos difíceis de rastrear.

@nvartolomei Veja a especificação da promessa ES6.

Este é um objetivo de longo prazo, não algo que está acontecendo imediatamente. Mas queremos rastrear o planejamento e as discussões em um único lugar e não por meio de comentários em todas as questões quando isso surgir. Estamos cientes do problema de as promessas atualmente não serem canceláveis, o que é um dos principais motivos de ainda não termos feito isso.

@yaycmyk Para simplificar demais uma questão muito complexa ... os comentários estão dizendo ... usar isMounted para evitar setState para componentes não montados na verdade não resolve o problema de setState warning estava tentando indicar - na verdade, ele apenas esconde o problema. Além disso, chamar setState como resultado de uma promessa é um pouco anti-padrão de qualquer maneira, já que pode causar condições de corrida que não aparecerão necessariamente nos testes. Portanto, queremos nos livrar dele e descobrir uma recomendação de "melhor prática" para usar promessas com o React.

Eu concordo que os problemas são um pouco inescrutáveis, mas em grande parte porque é um problema complexo que ainda estamos descobrindo e para o qual ainda não temos uma resposta pronta.

chamar setState como resultado de uma promessa é um pouco antipadrão de qualquer maneira, já que pode causar condições de corrida que não necessariamente aparecerão nos testes

Podemos concordar em discordar nisso. Há momentos em que o conteúdo está sendo buscado de forma assíncrona e você não quer ter que passar por um rerender em grande escala para inserir esse conteúdo depois de resolvido. Eu o uso especificamente em uma implementação de visualização de tabela infinita em que um renderizador virtual completo seria desnecessário.

Você pode não conseguir cancelar uma promessa, mas pode fazer com que ela cancele a referência do componente ao desmontar, assim:

const SomeComponent = React.createClass({
    componentDidMount() {
        this.protect = protectFromUnmount();

        ajax(/* */).then(
            this.protect( // <-- barrier between the promise and the component
                response => {this.setState({thing: response.thing});}
            )
        );
    },
    componentWillUnmount() {
        this.protect.unmount();
    },
});

A distinção importante é quando this.protect.unmount() é chamado em componentWillUnmount , todos os retornos de chamada são desreferenciados, o que significa que o componente é desreferenciado e, quando a promessa é concluída, ele apenas chama um ambiente autônomo. Isso deve evitar qualquer vazamento de memória relacionado a componentes não montados de referências de promessas. fonte para protectFromUnmount

Este método simples pode ser usado para adicionar cancel a qualquer promessa

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then((val) =>
      hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    );
    promise.catch((error) =>
      hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

EDITAR: Atualizado para correção / integridade.

COMO USAR

const somePromise = new Promise(r => setTimeout(r, 1000));

const cancelable = makeCancelable(somePromise);

cancelable
  .promise
  .then(() => console.log('resolved'))
  .catch(({isCanceled, ...error}) => console.log('isCanceled', isCanceled));

// Cancel promise
cancelable.cancel();

Listar maneiras de suprimir as promessas do ES6 para torná-las canceláveis ​​não vem ao caso. A intenção deve ser fornecer uma solução que funcione COM a especificação, em vez de tentar trabalhar EM VOLTA da especificação.

Eu concordo. Em vez de simplesmente verificar se o componente ainda está montado quando recebemos o resultado da promessa, temos que recorrer a todos os tipos de magia para que possamos "desvincular" nossa promessa do componente em que ela deve definir seu resultado, claramente lutando contra a forma como as promessas são projetados.
Para mim, parece uma superengenharia de uma solução em que um teste simples é a maneira mais fácil de cuidar disso.

Podemos manter a verificação simples apenas por:

React.createClass(function() {
  componentDidMount: function() {
    this._isMounted = true;

    ajax(/* */).then(this.handleResponse);
  }

  handleResponse: function(response) {
    if (!this._isMounted) return; // Protection

    /* */
  }

  componentWillUnmount: function() {
    this._isMounted = false;
  }
});

É claro que essa é minha opinião, mas me parece que o carregamento de dados assíncronos com uma promessa dentro de um componente react é um cenário tão comum que deveria ser coberto pelo react, em vez de ter que escrever nosso próprio código clichê.

O problema é que, para manter o estado de montagem verdadeiro, devemos adicionar ouvinte quando o react terminar o processo de montagem DOM em cada componente (o mesmo, que anexa o componentDidMount, se definido), mas isso afetará o perf, porque não precisamos para pousar em todos os lugares. O componente não escuta a montagem DOM pronta por padrão, pois o componentDidMount é indefinido.

E se setState pudesse receber uma promessa encadeada que resolve as mudanças de estado desejadas? Se o componente for desmontado, se houver alguma promessa pendente, seu resultado final será ignorado.

@istarkov bom padrão, goste! Aqui está a API ligeiramente alterada para isso:

// create a new promise
const [response, cancel] = await cancelable(fetch('/api/data'));

// cancel it
cancel();

Já que sou novo no React e na leitura de documentos, só para jogar isso por aí: a dica Carregar Dados Iniciais via Ajax usa .isMounted() , então o site discorda do site. Seria ótimo ver uma dica completa sobre como cancelar o carregamento inicial em componentWillUnmount , talvez usando o padrão de @istarkov acima.

@dtertman corrigido em https://github.com/facebook/react/pull/5870 , estará online quando os documentos forem selecionados.

@jimfb obrigado, não tenho certeza de como perdi isso na pesquisa.

@istarkov não tem certeza se isso foi intencional, mas seu makeCancelable não funciona se a promessa original falhar. Quando a promessa original é rejeitada, nenhum manipulador é chamado.

Isso não parece ideal porque você ainda pode querer lidar com um erro na promessa original.

Aqui está minha proposta de makeCancelable que lida com uma rejeição na promessa original:

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then((val) =>
      hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    );
    promise.catch((error) =>
      hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

Não tenho certeza de onde estou se fazer promessas canceláveis ​​é uma boa ideia, mas se vamos fazer promessas canceláveis, devemos preservar o comportamento subjacente :).

@vpontis : +1:

@istarkov, sua postagem original é referenciada aqui: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html

Quer atualizar sua postagem ou devo enviar uma mensagem ao autor da postagem?

@vpontis Obrigado, vou consertar! (https://github.com/facebook/react/pull/6152)

Ei @jimfb , divertido encontrar você na internet!

Outra correção de bug na função makeCancelable : ela pode causar UnhandledPromiseRejectionWarning em versões de nó recentes (particularmente ao executar testes com uma nova versão de nó). Uma das mudanças no nó 6.6.0 é que todas as rejeições de promessa não tratadas resultam em um aviso. O código existente de @vpontis tinha then e catch separadas na mesma promessa base. Efetivamente, isso cria _duas_ promessas, uma que lida apenas com o sucesso e outra que apenas lida com erros. Isso significa que, se houver um erro, a primeira promessa será vista pelo nó como uma rejeição de promessa não tratada.

A correção é muito fácil: apenas encadeie as duas chamadas de modo que faça uma promessa com um manipulador de sucesso e erro. Aqui está o código fixo:

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise
      .then((val) =>
        hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
      )
      .catch((error) =>
        hasCanceled_ ? reject({isCanceled: true}) : reject(error)
      );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

@alangpierce Isso está muito perto de ser correto, mas não exatamente; se resolve() ou reject() lançar sincronizadamente por qualquer motivo em uma promessa resolvida, ambos os manipuladores serão chamados.

A solução é usar o padrão .then(onFulfilled, onRejected) :

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then(
      (val) => hasCanceled_ ? reject({isCanceled: true}) : resolve(val),
      (error) => hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

esta solução makeCancelable não é efetivamente a mesma que a chamada isMounted () ao observar o ponto 3 para saber por que isMounted () foi descontinuado:

Chamar setState quando um componente estiver completamente desmontado.
Esta é uma forte indicação de que um retorno de chamada assíncrono não está sendo limpo corretamente. Infelizmente, as APIs JS convencionais tornam muito fácil evitar a limpeza de callbacks assíncronos pendentes.

Um retorno de chamada não é grande coisa. No entanto, esse retorno de chamada permanece em objetos e retornos de chamada intermediários, promessas e assinaturas. Se muitos dos seus componentes fizerem isso, você rapidamente terá problemas de memória

makeCancellable apenas cria outra promessa que acaba guardando uma referência para funções que guardam uma referência para o componente. a solução makeCancellable é apenas mover a propriedade booleana isMounted para a promessa.

a fim de resolver o problema do GC, você precisa anular algo quando cancel () está sendo chamado. caso contrário, você ainda terá uma cadeia de referência do processo assíncrono ao componente.

class CancellableDeferred {
  constructor(request) {
    this.deferred = $.Deferred();

    request.then((data) => {
      if (this.deferred != null) {
        this.deferred.resolve(data);
      }
    });

    request.fail((data) => {
      if (this.deferred != null) {
        this.deferred.reject(data);
      }
    });
  }

  cancel() {
    this.deferred = null;
  } 

  promise() {
    return this.deferred.promise();
  }
}

-> é como eu faria isso com objetos adiados jQuery. Eu realmente não estou familiarizado com a API Promise, então não sei como seria. Além disso, isso não rejeita o adiado quando cancel () foi chamado e o adiado não foi resolvido. Provavelmente, as pessoas têm uma opinião diferente sobre como isso deve funcionar.

então a corrente se parece com isto:

Solicitação AJAX -> Fechamento -> CancellableDeferredInstance -> JQuery Deferred -> Componente

então, após o cancelamento, fica assim:

Solicitação AJAX -> Fechamento -> CancellableDeferredInstance / referência de objeto agora null / JQuery Deferred -> Componente

portanto, a solicitação AJAX não está mais impedindo que o componente seja GCd [presumindo que eu não tenha bagunçado a implementação em algum lugar por acidentalmente manter uma referência como adiado. yay encerramentos ....]

Olá @benmmurphy , Não estou muito familiarizado com a compilação de lixo JS e pode funcionar de maneira diferente com o React, mas tenho um entendimento diferente.

makeCancellable permite que um componente React seja coletado como lixo quando for desmontado. Eu vou explicar.

makeCancellable apenas cria outra promessa que acaba guardando uma referência para funções que guardam uma referência para o componente. a solução makeCancellable é apenas mover a propriedade booleana isMounted para a promessa.

Sem makeCancellable :

handleError() {
  if (this.isMounted()) {
    console.log('ERROR')
  }
}

Com makeCancellable :

promise.then(...).fail((reason) => {
  if (reason.isCancelled) return;
  console.log('ERROR');
})

Sem makeCancellable você ainda tem uma referência a this portanto, o componente não pode ser coletado como lixo quando for desmontado. Mas, no outro caso, o manipulador de falha da promessa cancelável é chamado assim que o componente é desmontado, de forma que você não tem mais nenhuma referência por aí.

@vpontis

eu tenho algum código nodejs que ilustra o problema. O componente Foo só será submetido ao GC depois que o retorno de chamada assíncrono resolve for definido como nulo. Por exemplo, digamos que você dispare uma solicitação ajax que leva 30 segundos para ser resolvida e, em seguida, o componente é desmontado. Então, o componente não será GCd por 30s. Este é um dos problemas que eles estão tentando resolver com o uso de isMount () obsoleto.

npm install promise
npm install weak

node --expose-gc gc.js
first gc Foo {}
after first gc Foo {}
after resolve = null Foo {}
foo gc'd
after second gc {}

https://gist.github.com/benmmurphy/aaf35a44a6e8a1fbae1764ebed9917b6

EDITAR:

desculpe por falar além de você, mas a primeira vez que li a postagem não entendi o que você estava tentando fazer, mas agora acho que entendo. Acho que o que você está tentando dizer é que, como o retorno de chamada de erro não contém uma referência ao componente (ou não segue uma referência ao componente), o componente não é considerado referenciado pela promessa. Isso é verdade. Bem, a primeira parte é verdade. No entanto, existem problemas com este raciocínio:

1) mesmo que o manipulador de erros em seu exemplo não tenha uma referência ao componente, o retorno de chamada then() normalmente terá. Por exemplo, o identificador then normalmente fará this.setState(...) .
2) mesmo que o manipulador de erros em seu exemplo não tenha uma referência ao componente que a maioria dos manipuladores de erros terá. por exemplo, eles farão algo como:

promise.then(...).fail((reason) => {
  if (reason.isCancelled) return;
  console.log('ERROR');

  this.setState({error: true});
})

3) mesmo sabendo que o código não seguirá o retorno de chamada then() e sabemos que ele sairá da função após verificar a variável isCancelled , o GC não sabe disso.

e antes que alguém use meu exemplo ou algo baseado nele, certifique-se de testar se ele realmente funciona corretamente. ainda não testei o meu e não me surpreenderia se não funcionasse, porque cometi um erro bobo: /

em termos de API de promessa, isso funciona para mim em nodejs em termos de GC. entretanto, eu preferiria não ter os _resolve , _reject params perto dos fechamentos, porque não tenho certeza se isso funcionará de acordo com a especificação JS ou se simplesmente funcionará porque o nó está fazendo algumas otimizações. Uma implementação pode capturar todas as variáveis ​​visíveis ou apenas as variáveis ​​que são referenciadas no encerramento? Não sei, talvez alguém que realmente entenda JS possa intervir e explicar :)

var makeCancelable = (promise) => {
  let resolve;
  let reject;

  const wrappedPromise = new Promise((_resolve, _reject) => {
    resolve = _resolve;
    reject = _reject;

    promise.then((val) => {
       if (resolve != null) resolve(val)
    });
    promise.catch((error) => {
       if (reject != null) reject(error)
    });
  });

  return {
    promise: wrappedPromise,
    cancel() {
      resolve = null;
      reject = null;
    },
  };
};

A função isMounted será removida no 16.0?

Sugestão para uma pequena melhoria com o código @istarkov :

const makeCancelable = (promise) => {
    let hasCanceled_ = false
    promise.then((val) =>
        hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    )
    .catch((error) =>
        hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    )

    return {
        promise,
        cancel() {
            hasCanceled_ = true
        }
    }
}

É que a nova promessa é redundante.

É que a nova promessa é redundante.

@BnayaZil Você está chamando as resolve e reject , mas não está claro de onde são. Você quis dizer Promise.resolve e Promise.reject ? Nesse caso, você ainda estaria retornando uma nova promessa.

Alguns dias atrás, uma nova API foi adicionada à especificação do DOM que permite abortar as solicitações fetch (). Esta API ainda não foi implementada em nenhum navegador, mas criei um polyfill para ela disponível no NPM que era "abortcontroller-polyfill". O polyfill faz essencialmente a mesma coisa que o código postado por @istarkov, mas permite que você faça a transição sem alterações de código para a API do navegador real depois de implementada.

Detalhes aqui:
https://mo.github.io/2017/07/24/abort-fetch-abortcontroller-polyfill.html

Visto que React.createClass não existe mais no React 16 e o ​​novo pacote create-react-class inclui uma mensagem de depreciação clara para isMounted , vou encerrar isso.

Eu concordo com @benmmurphy que a solução de @istarkov é efetivamente igual a usar isMounted() pois não resolve o problema da coleta de lixo.

A solução de @benmmurphy está mais próxima, mas anula as variáveis ​​erradas para que os manipuladores de promessa não sejam referenciados.

A chave é passar uma função por meio do encerramento que cancela a referência aos manipuladores:

const makeCancelable = promise => {
  let cancel = () => {};

  const wrappedPromise = new Promise((resolve, reject) => {
    cancel = () => {
      resolve = null;
      reject = null;
    };

    promise.then(
      val => {
        if (resolve) resolve(val);
      },
      error => {
        if (reject) reject(error);
      }
    );
  });

  wrappedPromise.cancel = cancel;
  return wrappedPromise;
};

Mais explicações sobre por que essa solução permite a coleta de lixo e não as soluções anteriores podem ser encontradas aqui .

Fui em frente e transformei isso em um pacote npm , react , trashable-react .

Edit: meu mal, eu acabei de olhar para @hjylewis thrashable , e ele cancela promessas também. Ainda assim, o padrão abaixo é IMO uma pequena melhoria.

Nenhuma dessas soluções cancela promessas, que podem ser canceladas sem qualquer extensão, absorvendo uma promessa eternamente pendente.

function makeCancelable(promise) {
  let active = true;
  return {
    cancel() {active = false},
    promise: promise.then(
      value => active ? value : new Promise(()=>{}),
      reason => active ? reason : new Promise(()=>{})
    )
  }
}

// used as above:

const {promise, cancel} = makeCancelable(Promise.resolve("Hey!"))

promise.then((v) => console.log(v)) // never logs
cancel()

viver aqui

Pode haver sutilezas a serem resolvidas em relação ao GC e o café ainda está para fazer efeito, mas esse padrão garante que a promessa retornada seja realmente cancelada e não possa vazar (já implementei isso no passado).

@pygy Obrigado pela resposta!

Infelizmente, sua solução ainda não permite a coleta de lixo. Você essencialmente acabou de reescrever a solução de @istarkov que usa uma condicional.

Você pode testar isso facilmente descartando essa implementação em trashable e executando os testes (o teste de coleta de lixo falha).

Sua implementação também falha em lidar com os erros de maneira adequada.

Estamos em 2018. Existe uma abordagem ainda melhor do que a mencionada acima?

sim você pode usar alguns frameworks de navegação que têm uma documentação com o dobro do tamanho do react nativo, mas é muito profissional

Esses trechos para "cancelar" uma promessa não são tão bons IMHO. As promessas canceladas ainda não serão resolvidas até que a promessa original seja resolvida. Portanto, a limpeza de memória não acontecerá até que você use um truque isMounted.

Um wrapper de promessa cancelável adequado teria que usar uma segunda promessa e Promise.race. ou seja, Promise.race([originalPromise, cancelationPromise])

A solução de @benmmurphy está mais próxima, mas anula as variáveis ​​erradas para que os manipuladores de promessa não sejam referenciados.

Acho que minha solução funciona, mas não sei o suficiente sobre o que promete o tempo de execução do javascript para saber com certeza. Se você executar a solução sob o nó em seu equipamento de teste, ele GCs o valor corretamente. Minha solução atribuiu as funções de resolução / rejeição a um escopo mais alto e, em seguida, anulou esses valores quando cancel foi chamado. No entanto, as funções ainda estavam disponíveis no escopo inferior, mas não referenciadas. Acho que os motores de javascript modernos não capturam variáveis ​​em um encerramento, a menos que sejam referenciados. Acho que costumava ser um grande problema, onde as pessoas criavam acidentalmente vazamentos de DOM porque faziam coisas como: var element = findDOM (); element.addEventListener ('clique', função () {}); e o elemento seria referenciado no encerramento, embora não fosse usado no encerramento.

@hjylewis @benmmurphy por que precisamos desreferenciar os manipuladores ?? depois que os manipuladores são executados, a coleta de lixo acontece de qualquer forma, certo ??

Esses trechos para "cancelar" uma promessa não são tão bons IMHO. As promessas canceladas ainda não serão resolvidas até que a promessa original seja resolvida. Portanto, a limpeza de memória não acontecerá até que você use um truque isMounted.

Um wrapper de promessa cancelável adequado teria que usar uma segunda promessa e Promise.race. ou seja, Promise.race([originalPromise, cancelationPromise])

@hjylewis e meu você realmente trabalha, você pode verificá-lo com o nó fraco. mas olhando para eles novamente, eu concordo que nenhum deles é um código de promessa escrita idiossincrático. como um usuário de promessa, você provavelmente esperaria que uma promessa 'cancelada' fosse resolvida no estado rejeitado e nenhum dos dois faz isso. embora, possivelmente, no caso de um componente, esta seja uma solução que seria mais fácil de usar, porque você não precisa escrever código extra para ignorar o manipulador de rejeição.

Acho que uma promessa rejeitável idiossincrática usaria Promise.race ([]) para construir uma promessa cancelável. funciona porque quando uma promessa é resolvida, os retornos de chamada pendentes são excluídos, de modo que não haveria nenhuma cadeia de referência da rede do navegador para o seu componente, porque não haveria mais uma referência entre a promessa de corrida e o componente.

Estou curioso para saber se é de alguma forma possível usar Promise.all() com essas promessas canceláveis ​​e evitar erros não detectados no console do navegador ... porque sou capaz de detectar apenas o primeiro erro de cancelamento, outros permanecem não detectados.

Estamos em 2018. Existe uma abordagem ainda melhor do que a mencionada acima?

Qualquer abordagem melhor para cancelar a execução de uma promessa, ou seja, setTimeout, chamadas de API, etc. É 2019 😭 😞

Há um tópico de cancelamento de promessa em andamento no TC39, (eu acho) é relevante aqui (talvez .. não tenho certeza)
https://github.com/tc39/proposal-cancellation/issues/24

Qualquer abordagem melhor para cancelar a execução de uma promessa, ou seja, setTimeout, chamadas de API, etc. É 2019 😭 😞

Estamos procurando por algo como

const promise = new Promise(r => setTimeout(r, 1000))
  .then(() => console.log('resolved'))
  .catch(()=> console.log('error'))
  .canceled(() => console.log('canceled'));

// Cancel promise
promise.cancel();
Esta página foi útil?
0 / 5 - 0 avaliações