2013-08-20 29 views
14

Durante il tentativo di rispondere this question, ho incontrato un strano comportamento (che non è lo stesso: il suo è dovuto al troppo poche iterazioni, la miniera di troppo):Un ciclo 3,000,000,000 iterazioni si comporta stranamente

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!'; 
}; 

Il ciclo richiede pochi secondi per eseguire, a causa delle sue 3.000.000.000 di iterazioni.

Una volta che il pulsante viene premuto, quello che mi aspettavo:

  1. wait for it... appare
  2. il processo si blocca un po 'a causa del ciclo
  3. dary! appare

Cosa è realmente accaduto:

  1. il processo si blocca un po 'a causa del ciclo
  2. wait for it... dary! appare insieme

Qualsiasi idea del perché un tale comportamento?

Da solo: fiddle.

+1

Si comporta in questo modo in tutti i browser? Funziona correttamente se imposti un piccolo timeout – Huangism

+0

Ho visto il violino, è davvero strano .... e ho pensato che 'looping' è un processo plain-vanilla! –

+1

Anche i browser ottimizzano le modifiche al DOM/CSS in modo che vengano eseguite solo quando necessario (per evitare più riflussi consecutivi). –

risposta

17

Il motivo è che la funzione, nel suo complesso, si sta eseguendo in modo sincrono. Nel momento in cui si imposta l'output su wait for it..., esso entra nel ciclo di lunga durata e si accosta al thread. Se avvolgi il resto in un timeout, il primo testo apparirà come normale.

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); 
}; 

Si noti che continuerà a congelare l'interfaccia utente durante l'elaborazione.

Edit: Utilizzando 0 come il valore di ritardo in Chrome funziona, ma non in ultimo Firefox e IE 10. La modifica del valore di 10 opere in entrambi i casi.

+0

L'ho eseguito con il violino, è ancora in stampa "Aspettatelo ... dario!" Insieme! – Sid

+0

@Sid L'ho provato con il violino e ho avuto il comportamento corretto (usando chrome). [JSFiddle] (http://jsfiddle.net/2MrD8/3/). Modifica: appena testato in Firefox e lo ha mostrato insieme. se si cambia '0' a' 10', funziona come previsto. Sembra che firefox lo stia eseguendo in modo sincrono in alcuni casi. –

+0

Si potrebbe voler modificare questa riga 'output.textContent + = 'dary!'; }, 0); 'Ho impostato su 100 e ora funziona correttamente come previsto. controlla questo [fiddle] (http://jsfiddle.net/2MrD8/4/) – Sid

10

Javascript è praticamente single-threaded. Se stai utilizzando il codice, la pagina non risponde e non verrà aggiornata fino al completamento del codice. (Si noti che questo è specifico dell'implementazione, ma questo è il modo in cui tutti i browser lo fanno oggi.)

+0

Esiste un modo per * forzare * la pagina per essere aggiornata con un messaggio come descritto? – gibberish

+1

Inserire un ritardo, come indicato nelle altre risposte. –

0

L'unica spiegazione che vedo è che il browser aggiorna la vista dopo che il javascript è stato eseguito? Come prova, funziona come previsto:

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 e Simon Belanger hanno fornito spiegazioni per la causa; questo post discute una soluzione diversa. Tuttavia, questa soluzione NON è appropriata per un ciclo di iterazione da 3 miliardi poiché è troppo lenta per confronto.

Secondo this SO post by user Cocco, utilizzare setTimeout è meno ottimale di requestAnimationFrame per questo scopo.Quindi, ecco come utilizzare 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() 

Nota: Per mostrare accordo/apprezzamento, si prega di upvote Cocco's answer nel post sopra e non questo. Grazie.

1

Il codice è in esecuzione come previsto. Il problema è che il browser non mostrerà la tua modifica al documento, fino a quando non sarà terminato il javascript. Il time out sta risolvendo questo problema, interrompendo l'esecuzione del codice in due eventi separati. Il seguente codice mostrerà che cosa ti aspetti che stia accadendo.

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!'); 
}; 

È inoltre necessario fare attenzione quando si utilizza la soluzione di timeout, dal momento che l'esecuzione non è più sincrono.

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'; 
}; 

Se si esegue questa versione, si noterà che "epico" è prima di "dary".