2013-08-20 17 views
14

Starając się odpowiedzieć this question spotkałem dziwne zachowanie (który nie jest taki sam: jego jest ze względu na zbyt małą liczbę powtórzeń, kopalnia zbyt dużo):Pętla 3,000,000,000 iteracje zachowuje się dziwnie

HTML:

<button id="go">it will be legend...</button> 
<div id="output"></div> 

JS:

var output = document.getElementById('output'); 
document.getElementById('go').onclick = function() { 
    output.textContent += 'wait for it...'; 
    for (var i=0; i<3000000000; i++) { 
     var unused = i; // don't really care 
    } 
    output.textContent += ' dary!'; 
}; 

pętla trwa kilka sekund, aby wykonać, ponieważ jego 3,000,000,000 iteracji.

Po kliknięciu przycisku, czego się spodziewałem:

  1. wait for it... pojawia
  2. proces zawiesza się trochę z powodu pętli
  3. dary! pojawia

Co właściwie stało:

  1. proces zawiesza się trochę z powodu pętli
  2. wait for it... dary! pojawia razem

jakiś pomysł, dlaczego takie zachowanie?

Sprawdź samodzielnie: fiddle.

+1

Czy zachowuje się w ten sposób we wszystkich przeglądarkach? Działa poprawnie, jeśli ustawisz mały czas oczekiwania – Huangism

+0

Widziałem skrzypce, to naprawdę dziwne .... i pomyślałem, że 'looping' to proces czysto waniliowy! –

+1

Również przeglądarki optymalizują zmiany w DOM/CSS, aby były wykonywane tylko wtedy, gdy jest to konieczne (aby uniknąć wielokrotnego ponownego wprowadzania zmian). –

Odpowiedz

17

Powodem jest to, że funkcja jako całość wykonuje się synchronicznie. Do czasu ustawienia wyjścia na wait for it..., wchodzi w długą pętlę i wątek. Jeśli zawiniesz resztę w timeout, pierwszy tekst będzie wyglądał normalnie.

var output = document.getElementById('output'); 
document.getElementById('go').onclick = function() { 
    output.textContent += 'wait for it...'; 
    window.setTimeout(function() { 
    for (var i=0; i<3000000000; i++) { 
     var unused = i; // don't really care 
    } 
    output.textContent += ' dary!'; 
    }, 0); 
}; 

Należy pamiętać, że nadal będzie blokował interfejs podczas przetwarzania.

Edit: Korzystanie 0 jako wartość opóźnienia w Chrome działa, ale nie w najnowszej Firefox i IE 10. Zmiana wartości do 10 prac w obu przypadkach.

+0

Uruchomiłem to w skrzypcach, nadal drukuje "czekaj na to ... dary!" Razem! – Sid

+0

@Sid przetestowałem go w skrzypcach i miał prawidłowe zachowanie (przy użyciu chrome). [JSFiddle] (http://jsfiddle.net/2MrD8/3/). Edycja: Właśnie testowałem w firefoxie i pokazałem to razem. jeśli zmienisz "0" na "10", to działa zgodnie z oczekiwaniami. Wygląda na to, że Firefox wykonuje to synchronicznie w niektórych przypadkach. –

+0

Możesz edytować ten wiersz 'output.textContent + = 'dary!'; }, 0); 'Ustawiłem na 100 i teraz działa poprawnie zgodnie z oczekiwaniami. sprawdź to [skrzypce] (http://jsfiddle.net/2MrD8/4/) – Sid

10

Javascript jest prawie jednotomowy. Jeśli używasz kodu, strona nie odpowiada i nie będzie aktualizowana do czasu ukończenia kodu. (Zauważ, że jest to implementacja specyficzna, ale tak właśnie działają wszystkie przeglądarki dzisiaj.)

+0

Czy istnieje sposób na * wymuszenie * strony, która ma zostać zaktualizowana za pomocą wiadomości zgodnie z opisem? – gibberish

+1

Wprowadź opóźnienie, o czym wspomniano w innych odpowiedziach. –

0

Jedyne wyjaśnienie jakie widzę to przeglądarka odświeża widok po wykonaniu javascript? Jako dowód, działa to zgodnie z oczekiwaniami:

var output = document.getElementById('output'); 
document.getElementById('go').onclick = function() { 
    output.textContent += 'wait for it...'; 
    window.setTimeout(count, 100); 
}; 

function count() { 
    for (var i = 0; i < 3000000000; i++) { 
     var unused = i; // don't really care 
    } 
    output.textContent += ' dary!'; 
} 
2

Dark Falcon i Simon Belanger przedstawili wyjaśnienia sprawy; ten post omawia inne rozwiązanie. Jednak to rozwiązanie zdecydowanie NIE jest odpowiednie dla 3-miliardowej pętli iteracji, ponieważ jest zbyt powolne w porównaniu.

Według this SO post by user Cocco użycie w tym celu setTimeout jest mniej optymalne niż requestAnimationFrame.Tak więc, oto jak używać requestAnimationFrame:

jsFiddle example

$(document).ready(function() { 
    var W = window, 
     D = W.document, 
     i = 0, 
     x = 0, 
     output = D.getElementById('output'); 

    function b() { 
     if (x == 0) { 
      output.textContent = 'wait for it...'; 
      x++; 
     } 
     i++; 
     if (i < 300) { 
      //if (i > 20) output.textContent = i; 
      requestAnimationFrame(b); 
     } else { 
      D.body.style.cursor = 'default'; 
      output.textContent += ' dary!'; 
     } 
    } 

    function start() { 
     console.log(D) 
     D.body.style.cursor = 'wait'; 
     b(); 
    } 
    D.getElementById('go').onclick = start; 
}); //END $(document).ready() 

Uwaga: Aby pokazać umowy/uznanie, proszę upvote Cocco's answer w poście powyżej nie ten. Dziękuję Ci.

1

Twój kod jest wykonywany zgodnie z oczekiwaniami. Problem polega na tym, że przeglądarka nie wyświetli Twojej zmiany w dokumencie, dopóki nie skończy się javascript. Limit czasu rozwiązuje ten problem, przerywając wykonywanie kodu na dwa osobne zdarzenia. Poniższy kod pokaże, że to, czego się spodziewałeś, dzieje się.

var output = document.getElementById('output'); 
document.getElementById('go').onclick = function() { 
    console.log('wait for it...';) 
    for (var i=0; i<3000000000; i++) { 
     var unused = i; // don't really care 
    } 
    console.log(' dary!'); 
}; 

Należy również zachować ostrożność podczas korzystania z rozwiązania Timeout, ponieważ wykonanie nie jest już synchroniczne.

output = document.getElementById('output'); 
document.getElementById('go').onclick = function() { 
    output.textContent += 'wait for it...'; 
    window.setTimeout(function() { 
     for (var i = 0; i < 3000000000; i++) { 
     var unused = i; 
     // don't really care 
     } 
    output.textContent += ' dary!'; 
    }, 0); 
    output.textContent += ' epic'; 
}; 

Jeśli uruchomisz tę wersję, zauważysz, że "epicki" jest przed "pamiętnym".

Powiązane problemy