Estou tentando entender como o Ember RunLoop funciona e o que o faz funcionar. Eu olhei a documentação , mas ainda tenho muitas dúvidas sobre ela. Estou interessado em entender melhor como funciona o RunLoop para poder escolher o método apropriado dentro de seu espaço de nomes, quando tiver que adiar a execução de algum código para um momento posterior.
- Quando o Ember RunLoop é iniciado. Depende do roteador, visualizações ou controladores ou de outra coisa?
- quanto tempo leva aproximadamente (eu sei que é bastante bobo perguntar e depende de muitas coisas, mas estou procurando uma ideia geral, ou talvez se há um tempo mínimo ou máximo que um loop de execução pode levar)
- O RunLoop está sendo executado o tempo todo ou apenas indica um período de tempo do início ao fim da execução e pode não ser executado por algum tempo?
- Se uma visualização for criada a partir de um RunLoop, é garantido que todo o seu conteúdo chegará ao DOM no momento em que o loop terminar?
Perdoe-me se essas são perguntas muito básicas, acho que entendê-las ajudará noobs como eu a usar melhor o Ember.
Respostas:
Atualização 09/10/2013: Confira esta visualização interativa do loop de execução: https://machty.s3.amazonaws.com/ember-run-loop-visual/index.html
Atualização 5/9/2013: todos os conceitos básicos abaixo ainda estão atualizados, mas a partir deste commit , a implementação do Ember Run Loop foi dividida em uma biblioteca separada chamada backburner.js , com algumas diferenças de API muito pequenas.
Primeiro, leia estes:
http://blog.sproutcore.com/the-run-loop-part-1/
http://blog.sproutcore.com/the-run-loop-part-2/
Eles não são 100% precisos para o Ember, mas os principais conceitos e motivação por trás do RunLoop ainda se aplicam ao Ember; apenas alguns detalhes de implementação diferem. Mas, para suas perguntas:
Quando o Ember RunLoop é iniciado. Depende do roteador, visualizações ou controladores ou de outra coisa?
Todos os eventos básicos do usuário (por exemplo, eventos de teclado, eventos de mouse, etc.) iniciarão o loop de execução. Isso garante que todas as alterações feitas nas propriedades associadas pelo evento capturado (mouse / teclado / cronômetro / etc) sejam totalmente propagadas por todo o sistema de vinculação de dados do Ember antes de retornar o controle de volta ao sistema. Portanto, mover o mouse, pressionar uma tecla, clicar em um botão, etc., todos iniciam o loop de execução.
quanto tempo leva aproximadamente (eu sei que é bastante bobo perguntar e depende de muitas coisas, mas estou procurando uma ideia geral, ou talvez se há um tempo mínimo ou máximo que um loop de execução pode levar)
Em nenhum momento o RunLoop controlará quanto tempo está levando para propagar todas as alterações pelo sistema e, em seguida, interromperá o RunLoop após atingir um limite de tempo máximo; em vez disso, o RunLoop sempre será executado até a conclusão e não parará até que todos os timers expirados tenham sido chamados, as ligações propagadas e talvez suas ligações propagadas e assim por diante. Obviamente, quanto mais mudanças precisarem ser propagadas de um único evento, mais tempo o RunLoop levará para ser concluído. Aqui está um exemplo (bastante injusto) de como o RunLoop pode ficar atolado com a propagação de alterações em comparação com outro framework (Backbone) que não tem um loop de execução: http://jsfiddle.net/jashkenas/CGSd5/. Moral da história: o RunLoop é realmente rápido para a maioria das coisas que você gostaria de fazer no Ember, e é onde reside grande parte do poder do Ember, mas se você quiser animar 30 círculos com Javascript a 60 quadros por segundo, aí podem ser maneiras melhores de fazer isso do que confiar no RunLoop de Ember.
O RunLoop está sendo executado o tempo todo ou apenas indica um período de tempo do início ao fim da execução e pode não ser executado por algum tempo?
Ele não é executado o tempo todo - ele precisa retornar o controle ao sistema em algum ponto ou então seu aplicativo travaria - é diferente de, digamos, um loop de execução em um servidor que tem um
while(true)
e continua por infinito até o servidor recebe um sinal para desligar ... o Ember RunLoop não tem,while(true)
mas só é ligado em resposta a eventos de usuário / temporizador.Se uma visualização for criada a partir de um RunLoop, é garantido que todo o seu conteúdo chegará ao DOM no momento em que o loop terminar?
Vamos ver se podemos descobrir isso. Uma das grandes mudanças de SC para Ember RunLoop é que, em vez de alternar entre
invokeOnce
einvokeLast
(que você vê no diagrama no primeiro link sobre RL de SproutCore), Ember fornece uma lista de 'filas' que, no durante um loop de execução, você pode agendar ações (funções a serem chamadas durante o loop de execução) especificando a qual fila a ação pertence (exemplo da fonteEmber.run.scheduleOnce('render', bindView, 'rerender');
:).Se você olhar
run_loop.js
no código fonte, você vêEmber.run.queues = ['sync', 'actions', 'destroy', 'timers'];
, mas se você abrir o seu depurador de JavaScript no navegador em um aplicativo Ember e avaliarEmber.run.queues
, você obter uma lista mais completa de filas:["sync", "actions", "render", "afterRender", "destroy", "timers"]
. O Ember mantém sua base de código bastante modular e possibilita que seu código, assim como seu próprio código em uma parte separada da biblioteca, insira mais filas. Nesse caso, a biblioteca Ember Views insererender
eafterRender
enfileira, especificamente após aactions
fila. Vou entender por que isso pode ser em um segundo. Primeiro, o algoritmo RunLoop:O algoritmo RunLoop é praticamente igual ao descrito nos artigos de loop de execução SC acima:
.begin()
e.end()
, apenas no Ember, você deseja executar seu código dentroEmber.run
, que chamará internamentebegin
eend
para você. (Apenas o código de loop de execução interno na base de código Ember ainda usabegin
eend
, então você deve ficar comEmber.run
)end()
ser chamado, o RunLoop entra em ação para propagar cada mudança feita pelo pedaço de código passado para aEmber.run
função. Isso inclui a propagação de valores de propriedades vinculadas, renderização de alterações de visualização no DOM, etc. etc. A ordem em que essas ações (vinculação, renderização de elementos DOM etc.) são realizadas é determinada pelaEmber.run.queues
matriz descrita acima:sync
. Ele executará todas as ações que foram programadas nasync
fila peloEmber.run
código. Essas ações também podem programar mais ações a serem realizadas durante o mesmo RunLoop, e cabe ao RunLoop garantir que execute todas as ações até que todas as filas sejam liberadas. A maneira como ele faz isso é, no final de cada fila, o RunLoop examinará todas as filas previamente liberadas e verá se alguma nova ação foi agendada. Em caso afirmativo, ele deve começar no início da primeira fila com ações programadas não realizadas e limpar a fila, continuando a rastrear suas etapas e reiniciar quando necessário até que todas as filas estejam completamente vazias.Essa é a essência do algoritmo. É assim que os dados vinculados são propagados pelo aplicativo. Você pode esperar que, assim que um RunLoop for executado até a conclusão, todos os dados vinculados serão totalmente propagados. Então, e quanto aos elementos DOM?
A ordem das filas, incluindo aquelas adicionadas pela biblioteca Ember Views, é importante aqui. Observe isso
render
eafterRender
venha depoissync
, eaction
. Async
fila contém todas as ações para propagação de dados vinculados. (action
depois disso, é usado apenas esparsamente na fonte Ember). Com base no algoritmo acima, é garantido que, no momento em que o RunLoop chega àrender
fila, todas as ligações de dados terão concluído a sincronização. Isso ocorre por design: você não gostaria de realizar a tarefa cara de renderizar elementos DOM antessincronizar as vinculações de dados, já que isso provavelmente exigiria uma nova renderização dos elementos DOM com dados atualizados - obviamente, uma maneira muito ineficiente e sujeita a erros de esvaziar todas as filas RunLoop. Portanto, o Ember explora de forma inteligente todo o trabalho de vinculação de dados que pode antes de renderizar os elementos DOM narender
fila.Então, finalmente, para responder à sua pergunta, sim, você pode esperar que todas as renderizações DOM necessárias tenham ocorrido quando o tempo
Ember.run
terminar. Aqui está um jsFiddle para demonstrar: http://jsfiddle.net/machty/6p6XJ/328/Outras coisas para saber sobre o RunLoop
Observadores vs. Ligações
É importante observar que Observers e Bindings, embora tenham a funcionalidade semelhante de responder às mudanças em uma propriedade "observada", se comportam de maneira totalmente diferente no contexto de um RunLoop. A propagação de vinculação, como vimos, é agendada na
sync
fila para ser executada pelo RunLoop. Os observadores, por outro lado, são acionados imediatamente quando a propriedade observada é alterada, sem precisar ser programado primeiro em uma fila RunLoop. Se um Observer e um binding "observarem" a mesma propriedade, o observador sempre será chamado 100% do tempo antes que o binding seja atualizado.scheduleOnce
eEmber.run.once
Um dos grandes aumentos de eficiência nos modelos de atualização automática do Ember se baseia no fato de que, graças ao RunLoop, várias ações RunLoop idênticas podem ser reunidas ("depuradas", se preferir) em uma única ação. Se você olhar para os
run_loop.js
internos, verá que as funções que facilitam esse comportamento são as funções relacionadasscheduleOnce
eEm.run.once
. A diferença entre eles não é tão importante quanto saber que existem e como podem descartar ações duplicadas na fila para evitar muitos cálculos inchados e inúteis durante o loop de execução.E quanto aos temporizadores?
Mesmo que 'temporizadores' seja uma das filas padrão listadas acima, o Ember só faz referência à fila em seus casos de teste RunLoop. Parece que tal fila teria sido usada nos dias do SproutCore com base em algumas das descrições dos artigos acima sobre temporizadores sendo a última coisa a disparar. Em Ember, a
timers
fila não é utilizado. Em vez disso, o RunLoop pode ser ativado por umsetTimeout
evento gerenciado internamente (consulte ainvokeLaterTimers
função), que é inteligente o suficiente para percorrer todos os temporizadores existentes, disparar todos aqueles que expiraram, determinar o temporizador futuro mais antigo e definir um temporizador internosetTimeout
apenas para aquele evento, o que ativará o RunLoop novamente quando for disparado. Essa abordagem é mais eficiente do que ter cada chamada de timer setTimeout e despertar, uma vez que, neste caso, apenas uma chamada setTimeout precisa ser feita, e o RunLoop é inteligente o suficiente para disparar todos os diferentes timers que podem estar disparando ao mesmo Tempo.Mais debouncing com a
sync
filaAqui está um fragmento do loop de execução, no meio de um loop por todas as filas do loop de execução. Observe o caso especial da
sync
fila: porsync
ser uma fila particularmente volátil, na qual os dados estão sendo propagados em todas as direções,Ember.beginPropertyChanges()
é chamada para evitar que qualquer observador seja disparado, seguido por uma chamada paraEmber.endPropertyChanges
. Isto é sábio: se durante a liberação dasync
fila, é inteiramente possível que uma propriedade em um objeto mude várias vezes antes de descansar em seu valor final, e você não gostaria de desperdiçar recursos disparando observadores imediatamente a cada mudança .Espero que isto ajude. Eu definitivamente tive que aprender um pouco apenas para escrever isso, o que era mais ou menos o ponto.
fonte