Qual é a diferença entre $ evalAsync e $ timeout no AngularJS?

180

Estou usando o AngularJS há algum tempo e descobri a necessidade de usar $ timeout de vez em quando (parece ser geralmente iniciar um plugin jQuery).

Recentemente, tenho tentado entender melhor e mais profundamente o ciclo de digestão e deparei-me com a função $ evalAsync .

Parece que essa função produz resultados semelhantes aos $timeout, apenas você não atrasa. Toda vez que usei $timeoutisso foi com um atraso de 0, então agora estou me perguntando se deveria ter usado $evalAsync.

Existem diferenças fundamentais entre os dois? Que casos você usaria um sobre o outro? Gostaria de ter uma ideia melhor de quando usar qual deles.

dnc253
fonte

Respostas:

263

Recentemente, respondi essencialmente a esta pergunta aqui: https://stackoverflow.com/a/17239084/215945 (Essa resposta está vinculada a algumas trocas do github com Misko.)

Para resumir:

  • se o código estiver na fila usando $ evalAsync de uma diretiva , ele deverá ser executado depois que o DOM tiver sido manipulado pelo Angular, mas antes que o navegador seja processado
  • se o código é enfileirado usando $ evalAsync de um controlador , ele deve ser executado antes que o DOM seja manipulado pelo Angular (e antes do renderizador do navegador) - raramente você deseja isso
  • se o código estiver na fila usando $ timeout , ele deverá ser executado após o DOM ter sido manipulado pelo Angular e após a renderização do navegador (o que pode causar tremulação em alguns casos)
Mark Rajcok
fonte
15
Obrigado pela explicação. Uma coisa que eu não tenho certeza se entendi. Por que faz diferença se você está chamando $ evalAsync de um controlador ou diretiva? O asyncQueue não sabe se foi registrado a partir de um controlador ou diretiva, apenas o enfileira no escopo atual. Tem a ver com quando as coisas são executadas em um controlador vs um controlador? Eu só quero entender essa parte.
dnc253
@ dnc253, eu não olhei o código Angular, então não sei a resposta para sua (boa) pergunta. Espero que alguém possa comentar.
Mark Rajcok
15
"de uma diretiva" significa "da função de vinculação de uma diretiva"? Ou isso é verdade para o comportamento quando executado a partir do método de link ou controlador de uma diretiva?
SimplGy
5
sim, não está realmente claro o que "de uma diretiva" e "de um controlador" significam aqui
thorn̈
1
@ MarkRajcok, você pode esclarecer aqui: se o código estiver na fila usando $ evalAsync de uma diretiva, ele deve ser executado após o DOM ter sido manipulado pelo Angular - deve ser executado após o DOM ter sido manipulado por essa diretiva ou por outras diretivas?
precisa saber é o seguinte
59

Para aqueles que desenvolvem aplicativos complexos, esteja ciente de que há um impacto no desempenho de sua escolha. Além disso, gostaria de completar a resposta do Mark com mais detalhes técnicos:

  • $ timeout (retorno de chamada) aguardará a conclusão do ciclo de resumo atual (ou seja, atualização angular de todo o modelo e do DOM), em seguida, executará seu retorno de chamada - afetando potencialmente o modelo angular - em seguida, lançará um full $applyno escopo raiz do $ e redigirá tudo.

  • $ evalAsync (retorno de chamada) , por outro lado, adicionará o retorno de chamada ao ciclo de resumo atual ou próximo. O que significa que se você estiver dentro de um ciclo de resumo (por exemplo, em uma função chamada de alguma ng-clickdiretiva), isso não esperará nada, o código será executado imediatamente. Se você estiver em uma chamada assíncrona, por exemplo setTimeout, a, um novo ciclo de resumo ( $apply) será acionado.

Portanto, em termos de desempenho, é sempre melhor chamar $evalAsync, a menos que seja importante para você que a visualização esteja atualizada antes de executar seu código, por exemplo, se você precisar de acesso a algum atributo DOm, como largura dos elementos e afins.

Se você quiser obter mais detalhes sobre a distinção entre $ timeout, $ evalAsync, $ digest, $ apply, convido você a ler minha resposta nessa outra pergunta: https://stackoverflow.com/a/23102223/1501926

Leia também a documentação :

O $ evalAsync não garante quando a expressão será executada, apenas que:

  • ele será executado após a função que agendou a avaliação (de preferência antes da renderização do DOM).
  • pelo menos um ciclo $ digest será executado após a execução da expressão.

Nota: se essa função for chamada fora de um ciclo $ digest, um novo ciclo $ digest será agendado . No entanto, é recomendável sempre chamar o código que altera o modelo de dentro de uma chamada $ apply. Isso inclui código avaliado via $ evalAsync.

floribon
fonte
Você pode explicar por que $ timeout é necessário se eu precisar acessar algum atributo DOM. Digamos que se eu tiver <table width = "{{x}}"> A função watch do ng-bind não atualiza o atributo dom na memória, entendo que ela não terá a chance de redesenhar a exibição até que o ciclo de digestão termine.
Sridhar Chidurala
2
@SridharChidurala, porque o DOM (o "HTML") é atualizado durante o ciclo de resumo, você precisa aguardar que seja feito antes de poder ler as modificações. No entanto, isso é desencorajado pelo Angular, você deve ler xdiretamente no seu escopo, e não no DOM, para não precisar esperar nada. Além disso, você deve usar melhor o ng-stylecss do que a widthpropriedade obsoleta . Se precisar de mais ajuda, abra uma nova pergunta no StackOverflow.
floribon 18/09/2015