Qual é a melhor maneira de balancear a carga de aplicativos javascript? [fechadas]

4

Eu escrevo um monte de aplicativos javascript e em muitas circunstâncias diferentes, o navegador não responde ou dá um erro de "script lento". Mesmo seguindo as práticas recomendadas, inicializando grandes conjuntos de dados, animações complexas ou quando muitos manipuladores de eventos são disparados de uma só vez, eu preciso incluir extra setTimeouts ou requestAnimationFrames em torno de blocos de script para carregá-los. Parece que deve haver alguma maneira padrão de gerenciar a carga do navegador para grandes aplicativos de javascript.

Alguma ideia? Parece que deve haver designers de aplicativos JS pensando nisso, mas não consigo encontrar nada na Web ou na pilha.

editar

Eu aprecio toda a resposta. Minha pergunta é não "Como posso escrever Javascript eficiente?" qual parece ser a pergunta que as respostas atuais estão respondendo. Minha pergunta é: Como posso balancear a carga de vários segmentos sequenciais de javascripts sendo executados no mesmo navegador?

Estou bem ciente de todas as informações postadas nas respostas até o momento e aprecio os pôsteres, mas estou procurando um framework ou um padrão de design que equilibre a execução de scripts em um navegador. Esta questão é comum em muitos outros idiomas e, para devops, estou simplesmente fazendo a mesma pergunta para o javascript.

    
por BishopZ 03.05.2013 / 16:50
fonte

9 respostas

13

Você tem um período de tempo muito curto em suas mãos.

Antes de falar sobre tópicos:

O DOM é lento

Realmente.

Se o seu site for pesado para DOM, encontre formas de offshore nessas manipulações. Use transições CSS3, use canvas para animações, evite tintas desnecessárias , etc.

Também existem técnicas para torná-lo mais rápido, como DocumentFragments, usando elementos off-DOM, etc.

O JavaScript é executado em um único thread no navegador

Isso significa que seus scripts têm para serem muito rápidos se você quiser que o usuário não sinta qualquer tipo de atraso.

Você pode, por exemplo, dividir cálculos intensivos em pequenos fragmentos, por exemplo:

function findMax(arr){
   var currentMax = -Infinity;
   var i=0;
   setImmidiate(function() doWork{
       if(arr[i] > currentMax){
           currentMax = arr[i];
       }
       i++;
       if(i< arr.length){
           setImmidiate(doWork);
       }
   });
   return currentMax;
};

Espere, não vá ainda! Eu menti! Não é realmente único encadeado

O JavaScript não precisa mais executar um único thread no navegador! Se você oferece suporte a navegadores modernos, poderá usar o WebWorkers. . Isso permitirá que você execute scripts intensivos em encadeamentos em segundo plano e não interrompa o fluxo principal do programa.

Se você suporta navegadores modernos, essa é provavelmente a maneira correta de lidar com cálculos intensivos de CPU.

Os WebWorkers permitem que você use tópicos que você está acostumado a partir de outras linguagens, mas de uma forma de salvar (ator-system'ish). Você pode dividir o trabalho entre os funcionários e equilibrá-lo como faria em qualquer outra linguagem de programação.

Observação os funcionários da Web exigem o IE10, o Safari 4+, o Opera 10.6 +, o Chrome 3+ ou o Firefox 3.5 +

Uma última coisa

O JavaScript foi projetado para executar E / S assíncronas, embora isso não seja o que você está fazendo com mais frequência. Os problemas de desempenho do JavaScript podem ser o resultado do código síncrono em que o código assíncrono é apropriado. Manipulação de eventos síncronos, código de bloqueio e, mais geralmente, qualquer coisa que não seja intensiva em CPU deve levar muito pouco tempo e não exigir o uso de 'grandes armas' como os trabalhadores da Web.

    
por 03.05.2013 / 17:14
fonte
5

Você não deveria estar escrevendo JavaScript sincronizado. JavaScript é único encadeado no navegador. O que você está fazendo não é o balanceamento de carga - consulte Load Balancing .

Quais práticas recomendadas você está usando? O JavaScript é bem rápido nos navegadores modernos e, se você estiver usando até mesmo as práticas recomendadas mais simples, a menos que esteja fazendo algo incrivelmente complexo, tudo bem.

    
por 03.05.2013 / 17:00
fonte
3

Se um script está lento (assumindo o código do lado do cliente aqui), geralmente não é baixo para JS ser lento. Seus gargalos são muito mais prováveis de serem encontrados em outro lugar. A API DOM notoriamente lenta, por exemplo.
Assim que você fala sobre manipulações DOM (como animação), você certamente vai notar uma queda na velocidade.

Muitas pessoas têm (e continuam a) criticar a API do DOM por serem mal projetadas, excessivamente complexas e lentas.
O principal problema com a API do DOM é que ela não é controlada pelo ECMA, como o JavaScript é, mas pelo W3. Simplificando: o JS do lado do cliente é geralmente composto de 60 a 75% de manipulações do DOM, mas, para eles, o JS depende de uma API mantida e desenvolvida por terceiros. Eu acho que é bastante óbvio que é apenas uma receita para slow-food.

Ainda assim, se você precisar realizar muitas computações longas e complexas, um Worker permite que você (classifique) crie um segmento em segundo plano, que dispara um evento após a conclusão.

Outro gargalo que pode ser notado é o uso excessivo de libs / toolkits, como jQuery. Eles geralmente não são muito modulares em termos de permitir que você inclua apenas os bits que realmente precisa e trazem consigo uma sobrecarga adicional. Comparar

document.getElementById('foo');//calls DOM API

para

$('#foo');//calls jQuery init, a few if's and else's to make up what to do
          //then string is passed to querySelector (DOM API), 
          //new jQ object is constructed (which also constructs an array)
          //return new jQObject

Pelo menos, foi o que aconteceu nos bastidores do jQuery.
Enquanto eu estou no assunto de jQ: um monte de código jQ realmente liga muitos ouvintes de eventos, trazendo-nos para um terceiro (e, por enquanto, último) possível gargalo.

Os ouvintes de eventos são verificados em um loop infinito, como você provavelmente sabe. Se você tiver um ouvinte, não notará nenhuma perda no desempenho. Se você vincular 1.000 manipuladores individuais diretamente a referências a nós individuais, você fará isso. Cada referência é mantida na memória, todos os manipuladores (objetos de função) também são.
A delegação de eventos não é tão difícil e pode tornar o evento muito mais rápido, como eu descobri aqui

    
por 03.05.2013 / 17:24
fonte
3

Além de quaisquer técnicas específicas, todas as ferramentas modernas de desenvolvimento de navegadores têm profilers JavaScript. Ligue-os e execute algumas tarefas e veja quais funções estão demorando mais tempo. Ataca cada gargalo um de cada vez.

    
por 03.05.2013 / 19:27
fonte
1

Como muitos outros já disseram, você não pode realmente "carregar o equilíbrio" javascript. A menos que você esteja usando trabalhadores, o javascript faz uma coisa de cada vez. O melhor que você pode esperar é trabalhar para obter tarefas fora da fila mais rapidamente do que elas estão chegando. Veja algumas coisas para ver:

Tenha cuidado com o DOM

Uma grande parte disso é, como as outras respostas dizem, evitando redesenhos de DOM, pois podem ser extremamente caras. Tente fazer todas as leituras do DOM em uma etapa e todas as suas gravações em outra, pois a leitura do DOM faz com que ele desenhe todas as alterações na fila para garantir que ele tenha informações atualizadas. Se você estiver usando as informações para fazer mais alterações no DOM, elas serão redesenhadas novamente e o usuário nunca verá o primeiro redesenho.

Evite setInterval

Se a função de retorno de chamada demorar mais do que o intervalo a ser executado, você obterá várias cópias da função de retorno de chamada na fila, e seu código ficará cada vez mais distante enquanto é executado. É por isso que muitas pessoas sugerem usar um setTimeout que se define quando é feito. Dessa forma, sempre há apenas uma instância do retorno de chamada aguardando na fila.

Eventos "Explosão" de buffer

Alguns eventos são acionados várias vezes ao mesmo tempo, mas na verdade você só deve reagir à última ocorrência anterior. Um exemplo disso que eu vi frequentemente é um ouvinte de evento de rolagem. Quando o usuário percorre a página, esse evento pode disparar várias vezes por segundo e, se o ouvinte fizer algum trabalho real, a criação da fila será mais rápida do que a compensação. É por isso que você vê padrões como esse:

var scrollTimer;

function scrollHandler(e) {
    // Actually handle the scrolling, DOM manipulations, real work...
}

window.addEventListener('scroll', function (e) {
    clearTimeout(scrollTimer);
    scrollTimer = setTimeout(scrollHandler, 100, e);
});

Dessa forma, não importa quanto o usuário rola, o código pesado não é executado até que eles parem de rolar (neste caso por um décimo de segundo).

Eventos contínuos de cache

Descobri que, em algumas situações, certos eventos ocorrem quase continuamente e têm vários ouvintes. Se você está reagindo ao acelerômetro, GPS, controladores de jogos (ou teclados sendo usados como controladores), então os eventos provavelmente chegarão o mais rápido possível, acionando seus ouvintes quase que constantemente. Tudo está tentando acontecer o mais rápido possível, e isso diminui a velocidade . Ao contrário dos eventos "burst", no entanto, você não pode simplesmente esperar que esses eventos parem antes de reagir. O ponto principal é reagir enquanto eles mudam.

Se você pensar sobre isso, seu código só precisa reagir uma vez por quadro. Tenha um único ouvinte keydown / keyup que defina sinalizadores para as teclas desejadas. Tenha uma única atualização de listener de acelerômetro e matriz com dados de orientação, ou um ouvinte de GPS armazene a latitude e a longitude. Esses ouvintes devem ser rápidos o suficiente para manter a fila vazia o mais rápido possível. Com isso, você pode percorrer todos os seus objetos uma vez em um quadro e apenas fazer com que eles verifiquem os sinalizadores e os arrays para obter as informações de que precisam.

    
por 09.05.2013 / 08:00
fonte
1

Portanto, com base nas suas edições, li isso como "como dividir efetivamente um cálculo de longo prazo em vários funcionários da Web". Eu tenho escrito uma biblioteca para lidar com trabalhadores da web e aqui está o que eu encontrei, existem algumas maneiras diferentes que você pode fazer isso, que muitas vezes dependem nos dados. Supondo que você tenha uma matriz que deseja processar usando n trabalhadores.

  1. Você pode simplesmente dividir arbitrariamente o array em n partes e enviar uma parte para cada trabalhador. Cortando o array (se for pequeno) ou iterando pela lista e enviando cada parte dos dados para um trabalhador diferente (via Math.random () ou%). Se seus itens de dados tiverem um tempo razoavelmente uniforme para processar, isso será tão rápido quanto uma fila, mas não terá a sobrecarga.
  2. Você pode configurar uma fila para que, após o processamento de um dado, o funcionário envie o resultado de volta e obtenha a próxima peça. Isto irá balancear a carga muito bem e significa que se um dado de dados levar 500ms e o resto demorar 1ms você não terá dados esperando atrás da peça de 500ms. Mas agora você tem funcionários aguardando para obter os dados e outras complexidades adicionadas que podem levar a tempos mais lentos do que os primeiros tipos de fila, mas também mais rápidos em outros, especialmente se você usar objetos transferíveis para acelerar os tempos de transferência.
  3. Você pode fazer apenas um trabalhador. Isso não é realmente uma solução para a pergunta que você fez, mas pode ser uma solução para o problema que você tem, acho que é realmente uma boa ideia checar se a sobrecarga de dividir seus dados em várias partes não supera a benefícios que você recebe. Especialmente considerando que, como o Chrome não oferece suporte a trabalhadores que fazem mais funcionários, você precisa fazer a divisão no encadeamento do DOM.
por 28.05.2013 / 14:36
fonte
1

O termo balanceamento de carga é confuso aqui e acho que isso levou a algumas respostas que você não considera úteis.

Primeiramente, todos os conselhos dados nas outras respostas são sólidos. Por favor, ouça os outros especialistas aqui.

Agora, para o seu balanceamento de carga. Equilíbrio de carga significa criar vários funcionários e distribuir o trabalho para eles de forma transparente. Tradicionalmente, isso significa que vários servidores atendem a solicitações de clientes. Mas vejo como isso se aplica ao seu problema de desempenho do JavaScript aqui.

Parece que você está tentando ver se pode dividir seu código em vários processos ou encadeamentos para que eles possam ser executados em paralelo. Dois problemas com isso:

  1. Paralelizar algo não o acelera tão drasticamente quanto você pode pensar e adiciona complexidade / risco e às vezes até penalidades de desempenho à medida que você adiciona pontos de sincronização ao redor do seu seções críticas .
  2. O JavaScript é de segmentação única. Mesmo que você queira correr o risco de criar vários segmentos, não é possível.

Dito isto, existe uma solução possível. Existe uma nova tecnologia chamada web workers, projetada para passar e manusear mensagens. Isso provavelmente pode lhe dar o que você precisa. No entanto, não acredito (pode ser confundido aqui) que seja suportado por todos os navegadores.

link

O site de desenvolvedores da Mozilla tem boas informações sobre problemas de segurança de threads. Leia sobre isso antes de seguir este curso.

link

    
por 29.05.2013 / 03:39
fonte
0

Portanto, há algumas respostas prolixas aqui que, na verdade, provavelmente estão todas corretas. No entanto, para o seu site em particular, considero bastante provável que existam de 1 a 5 funções que roubem 99% do tempo da CPU e que possam ser 80% otimizadas com algum trabalho.

A maioria dos navegadores tem ferramentas de criação de perfil de código que permitem observar o código enquanto você faz algo lento e, em seguida, informa o que o navegador passou a maior parte do tempo fazendo. O que estiver no topo da lista é o que você deve focar primeiro.

O material sobre trabalhadores da web, por exemplo, é realmente legal. Mas, em uma página básica usando apenas JavaScript para uma melhor experiência do usuário (em oposição a fazer algum tipo de trabalho real) eu ficaria surpreso se for necessário.

    
por 28.05.2013 / 15:19
fonte
0

Para abordar o aspecto de "balanceamento de carga" de sua pergunta, eu diria que é impossível para o JavaScript baseado em navegador.

"Balanceamento de carga" significa que você tem mais de uma máquina para trabalhar. Ou seja Se estiver implantando um serviço RESTful, você terá muitas máquinas executando o mesmo serviço e distribuindo o tráfego entre elas. Essa ideia realmente não se aplica ao JavaScript em execução nos navegadores (node.js é uma história diferente) porque você só tem uma máquina cliente. Não há como distribuir a carga para outras máquinas.

"Threading" é provavelmente uma analogia melhor (como outros já mencionaram). Em geral, o encadeamento não está realmente disponível em JavaScript, mas você pode usar os Web Workers HTML5 para obter um resultado semelhante ... mas, em seguida, você está restrito a navegadores compatíveis.

    
por 29.05.2013 / 01:21
fonte