Âmbito $ angular. $ Apply vs $ timeout como um $ seguro aplicável

Estou tentando entender melhor as nuances do uso do serviço $ timeout em Angular como uma espécie de método “safe $ apply”. Basicamente, em cenários em que um trecho de código poderia ser executado em resposta a um evento Angular ou a um evento não angular, como jQuery ou algum evento DOM padrão.

Pelo que entendi as coisas:

  1. Quebra automática de código em $ scope. $ Apply funciona bem para cenários em que você ainda não está em um loop digest (também conhecido como evento jQuery), mas gerará um erro se um resumo estiver em andamento
  2. Quebra automática de código em uma chamada $ timeout () sem parâmetro de atraso funciona se já está em um ciclo de digitação ou não

Olhando para código-fonte Angular, parece que $ timeout faz uma chamada para $ rootScope. $ Apply ().

  1. Por que o $ timeout () também gera um erro se um ciclo de digitação já está em andamento?
  2. É a melhor prática usar o $ scope. $ Apply () quando você tem certeza de que um resumo não estará em progresso e $ timeout () quando precisar que ele seja seguro de qualquer forma?
  3. É $ timeout () realmente aceitável “seguro aplicar”, ou existem dicas?

Obrigado por qualquer insight.

Olhando para código-fonte Angular, parece que $ timeout faz uma chamada para $ rootScope. $ Apply ().

  • Por que o $ timeout () também gera um erro se um ciclo de digitação já está em andamento?

$timeout faz uso de um $browser serviço angular não documentado. Especificamente, ele usa $browser.defer() que adia a execução de sua function de forma assíncrona via window.setTimeout(fn, delay) , que sempre será executado fora do ciclo de vida Angular. Apenas uma vez window.setTimeout disparou sua function, $timeout chamará $rootScope.$apply() .

  • É a melhor prática usar o $ scope. $ Apply () quando você tem certeza de que um resumo não estará em progresso e $ timeout () quando precisar que ele seja seguro de qualquer forma?

Eu diria que sim. Outro caso de uso é que às vezes você precisa acessar uma variável $ scope que você sabe que só será inicializada após o resumo. Exemplo simples seria se você quiser definir o estado de um formulário para sujo dentro do construtor do controlador (por qualquer motivo). Sem $ timeout, o FormController não foi inicializado e publicado no $ scope, portanto, $scope.yourform.setDirty() dentro de $ timeout garante que o FormController tenha sido inicializado. Claro que você pode fazer tudo isso com uma diretiva sem $ timeout, apenas dando outro exemplo de caso de uso.

  • É $ timeout () realmente aceitável “seguro aplicar”, ou existem dicas?

Deve ser sempre seguro, mas seu método deve sempre apontar para $ apply () na minha opinião. O aplicativo Angular atual em que estou trabalhando é bastante grande e só tivemos que confiar em $ timeout uma vez, em vez de $ apply ().

Se usarmos $ apply fortemente no aplicativo, poderemos obter o erro: $ digest já em andamento. Isso acontece porque um ciclo $ digest pode ser executado por vez. Podemos resolvê-lo por $ timeout ou por $ evalAsync.

O $ timeout não gera erro como “$ digest já em progresso” porque $ timeout diz ao Angular que após o ciclo atual, há um timeout aguardando e assim garante que não haverá colisões entre ciclos de digitação e, portanto, saída de $ O tempo limite será executado em um novo ciclo $ digest.

Eu tentei explicá-los em: Comparação de aplicar, timeout, digest e evalAsync .

Talvez isso te ajude.

Até onde eu entendi, $timeout é um wrapper em torno de setTimeout que implicitamente chama $scope.$apply , o que significa que ele é executado fora do ciclo de vida angular, mas kickstarts o próprio ciclo de vida angular. A única “pegadinha” em que posso pensar é que, se você está esperando que seu resultado esteja disponível neste $digest , você precisa encontrar outra maneira de “aplicar com segurança” (que, AFAIK, só está disponível via $scope.$$phase ).