Ajax simultaneidade
-
21-09-2019 - |
Pergunta
Eu tenho um aplicativo da web onde há um cronômetro que está constantemente contando. Enquanto isso, o cliente freqüentemente verifica com o servidor para ver se mais tempo foi adicionado ao timer. O código se parece com o seguinte:
function tick() {
// This function is called once every second
time -= 1;
redisplay(time);
};
function update(newtime) {
// This function is called whenever the ajax request
// to the server yields a new time
time = newtime;
};
É, é claro, um pouco mais complexo que isso, mas você pode ver a condição de raça inerente. E se a atualização e a função de carrapato estiverem tentando modificar time
ao mesmo tempo?
Francamente, não sei JavaScript suficientes para entender como lidar com esse tipo de questão de simultaneidade: existe uma maneira fácil de fazer isso, ou se não, alguém pode me apontar para recursos onde posso aprender mais?
Obrigada.
Solução
Você não tem uma condição de corrida, porque o JavaScript não é executado simultaneamente.
Toda vez que um retorno de chamada é demitido de uma operação assíncrona (Ajax, setTimeout
, etc), esse retorno de chamada deve terminar de executar antes que outro retorno de chamada de outra operação assíncrona possa ser chamado. Então se update()
está em execução, nenhum outro javascript é. Uma vez update()
Acabamentos, outros retornos de chamada das operações assínconais podem ser demitidos (por exemplo, tick()
). Curiosamente, é também por isso setTimeout
E sua série não é garantida para executar no momento exato em que o tempo limite é alcançado: algum outro JavaScript pode estar bloqueando a execução do retorno de chamada.
Verificação de saída http://ejohn.org/blog/how-javascript-timers-work/ Para obter boas informações sobre como tudo isso funciona.
Outras dicas
JavaScript é um único rosqueado. Não há condição de corrida. Não há como JavaScript para as duas linhas time = newtime;
e time -= 1;
para se sobrepor durante a execução. De fato, as duas funções são garantidas para não se sobrepor. Um deles será executado e o outro será executado.
Eu estava errado: isso não resolve o problema! (Explicação após o código)
NEWTIME = false;
function tick() {
// This function is called once every second
if (NEWTIME) {
time = NEWTIME;
NEWTIME = false;
}
time -= 1;
redisplay(time);
};
function update(newtime) {
// This function is called whenever the ajax request
// to the server yields a new time
NEWTIME = newtime;
};
O problema com esta solução errada é que fazer dessa maneira você apenas move o problema da condição de corrida da variável time
para variável NEWTIME
.
Apenas pense o seguinte: a execução de tick
alcança e executa a linha time = NEWTIME;
Agora, antes de continuar, a atualização é chamada e NEWTIME
recebe um valor X
. Agora a execução do carrapato continua executando NEWTIME = false;
. Dessa forma, você perdeu o X
valor de NEWTIME
E assim, o efeito de uma atualização () ligue!
O problema precisa de alguns semáforos. Faço muito isso por enviar Ajax após o acabamento anterior. Seu caso é um pouco parecido;)
Experimente algo assim. Deve ignorar todas as tentativas de diminuir o tempo que colide com o retorno de chamada do Ajax.
window.TimeKeeper = new function(){
this.time=0; //initial value, whatever
this.isopen=true;
this.decrement = function(){
if(this.isopen){
this.time--;
redisplay(this.time);
}
}
this.set = function(val){
if(this.isopen){
this.isopen=false;
this.time=val;
this.isopen=true;
} else {
//another AJAX callback is modifying time.
//You can enqueue current value and put it later
}
}
}
function tick() {
TimeKeeper.decrement();
};
function update(newtime) {
TimeKeeper.set(newtime);
};
Mais uma coisa - o Settimeout funciona como um novo thread e eu esperaria que os navegadores façam o acesso ao MEM sincronizador, por isso pode ser suficiente verificar se o valor não cresceu antes da diminuição. Mas a solução acima é mais flexível e segura.
E uma pequena dica - evite a consulta com o Ajax com muita frequência - pode causar problemas extras - como solicitações que voltam em ordem diferente da enviada e o uso da memória do Firefox aumentando muito o Ajax por um minuto;)
Contanto que você adicione mais alguns segundos à sua hora atual, você deve ficar bem.
Em vez de fazer time = newtime;
tentar time += newtime;
Dessa forma, você não perderá o segundo em que está preocupado.
Ainda assim, você está perdendo apenas um segundo na pior das hipóteses.