2012-12-28 11 views
7

Ho un ciclo che deve essere eseguito 200 milioni di volte in un browser. È un simulatore che molte persone devono usare regolarmente. Ci vogliono circa 15 minuti per funzionare, ma durante questo periodo, i browser visualizzeranno frequentemente un avviso con "questo script sta richiedendo troppo tempo" ecc., E blocca completamente Firefox durante la funzione. Ciò significa anche che la pagina non aggiorna il mio indicatore di stato (che è solo un numero).Impedisci a JavaScript di bloccare il browser sul grande loop

Ho cercato su google "javascript yield" e ho letto le prime 4 pagine di risultati. Alcuni discutono una nuova parola chiave "rendimento", ma c'è solo una descrizione ed un esempio, che trovo incomprensibile, ad es. "La funzione che contiene la parola chiave yield è un generatore: quando lo chiami, i parametri formali sono legati ad argomenti reali, ma il suo corpo non viene valutato". yield cede all'interfaccia utente?

Una delle poche soluzioni che ho trovato è questo vecchio post, che utilizza l'argomento callee deprecato e un timer di chiamarsi: http://www.julienlecomte.net/blog/2007/10/28/

Tuttavia, l'esempio di cui sopra non contiene alcuna variabile loop o di stato, e quando aggiungo questi cade a pezzi e il mio risultato netto è sempre zero.

Inoltre non esegue il chunking, ma ho trovato alcuni altri esempi che utilizzano "indice% 100 == 0" su ogni iterazione. Tuttavia, questo sembra essere un modo lento di farlo. Per esempio. questo:

How to stop intense Javascript loop from freezing the browser

Ma non ha alcun modo per aggiornare i progressi, e non cede alla UI (così si blocca ancora il browser). Ecco una versione di prova che pende il browser durante l'esecuzione:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<script> 
var spins = 1000000 
var chunkSize = 1000; 
var chunk; 
function Stats() {this.a=0}; 
var stats = new Stats();    
var big; 

var index = 0; 

var process = function() { 
    for (; index < spins; index++) { 
    stats.a++; 
    big = (big/3.6)+ big * 1.3 * big/2.1; 
    console.write(big); 
    // Perform xml processing 
    if (index + 1 < spins && index % 100 == 0) { 
     document.getElementById("result").innerHTML = stats.a; 
     setTimeout(process, 5); 
    } 
    } 
    document.getElementById("result").innerHTML = stats.a; 
}; 


</script> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>Untitled Document</title> 
</head> 

<body onload="process()"> 
<div id=result>result goes here.</div> 
</body> 
</html> 

e qui è un altro tentativo che il stats.a è sempre zero (quindi presumo ci sia qualche problema di scoping):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<script> 
var spins = 1000000 
var chunkSize = 1000; 
var chunk; 
function Stats() {this.a=0}; 
var stats = new Stats();    

function doIt() { 
    function spin() { 
     for (spinIx=0; (spinIx<chunkSize) && (spinIx+chunk < spins); spinIx++) { 
      stats.a++; 
     } 
    }   

    for (chunk =0; chunk < spins; chunk+=chunkSize){ 
     setTimeout(spin, 5); 
      document.getElementById("result").innerHTML = stats.a; 
     } 
     document.getElementById("result").innerHTML = stats.a; 
} 

</script> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>Untitled Document</title> 
</head> 

<body onload="doIt()"> 
<div id=result>result goes here.</div> 
</body> 
</html> 

I' Ho passato 48 ore cercando di farlo funzionare - o sono molto stupido o questo è molto difficile. Qualche idea?

Diverse persone hanno suggerito i web worker. Ho provato diversi giorni a farlo funzionare, ma non sono riuscito a trovare un esempio simile che superi un numero ecc. Il codice seguente era il mio ultimo tentativo di farlo funzionare, ma il risultato è sempre 0 quando dovrebbe essere 100000. I. fallisce nello stesso modo in cui il mio secondo esempio sopra fallisce.

spinMaster.html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
</head> 
<body> 
<script> 
if(typeof(Worker)==="undefined") { 
    document.write("<h1>sorry, your browser doesnt support web workers, please use firefox, opera, chorme or safari</h1>"); 
    } 
var worker =new Worker("spinWorker.js"); 

worker.postMessage({times:1000000}); 

worker.onmessage=function(event){ 
document.getElementById("result").innerHTML=event.data; 
}; 
</script> 

<div id="result">result goes here</div> 
</body> 
</html> 

spinWorker.js

function State() { 
    this.a=0; 
} 

var state = new State(); 

self.addEventListener('message', spin, false); 

function spin(e) { 
    var times, i; 

    times = e.data.times; 
//times = 1000000; // this doesnt work either. 

    for(i;i<times;i++) { 
     state.a++; 
    } 

    self.postMessage(state.a); 
} 

uscita risultante: 0

+4

dal JS è a thread singolo di solito, non credo che ci sia alcun modo intorno ad esso. Se stai supportando solo i browser più recenti, tuttavia, potresti voler esaminare i web worker, che possono generare una nuova discussione per lavorare: https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers – kennypu

+0

@kennypu Dovresti postarlo come risposta. –

+1

Il tuo problema di prestazioni potrebbe benissimo essere la costante manipolazione DOM (ogni 5 ms), che è MOLTO costoso. Prova ad aggiornare il DOM con una frequenza diversa, diciamo ogni 10-20 secondi e verifica se ciò lo rende migliore o più veloce. – elclanrs

risposta

1

poiché JS è singolo filettato solito, non credo ci sia alcun modo per aggirare esso. Tuttavia, se stai supportando solo i browser più recenti, dovresti esaminare i web worker, che possono generare una nuova discussione per lavorare: http://developer.mozilla.org/en-US/docs/DOM/Using_web_workers

l'unico lato negativo che riesco a pensare è che ho letto che è difficile eseguire il debug di Web Workers perché gli strumenti di sviluppo (gli strumenti di sviluppo di Chrome a meno che non si esegua il canale di sviluppo, firebug), non supportano la creazione di profili per questo.

ecco un bel tutorial per iniziare: http://net.tutsplus.com/tutorials/javascript-ajax/getting-started-with-web-workers/

4

lavoratori Web suonano come la soluzione migliore.

Ho scritto questo rapidamente, quindi non so se funzionerà. Le prestazioni saranno pessime ...

Modifica: modificato nello stesso formato del poster. Testato

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<script> 
    var spins = 1000000 
    var chunkSize = 1000; 
    var chunk; 
    function Stats() {this.a=0}; 
    var stats = new Stats();   
    var big = 0.0; 

    var index = 0; 

    function workLoop() { 

     index += 1; 

     stats.a++; 
     big = (big/3.6)+ big * 1.3 * big/2.1; 
     console.log(big); 
     // Perform xml processing 
     document.getElementById('result').innerHTML = stats.a; 
     if (index < spins) { 
      setTimeout(workLoop, 5); 
     } 

    } 



</script> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>Untitled Document</title> 
</head> 

<body onload="workLoop()"> 
<div id="result">result goes here.</div> 
</body> 
</html> 
+0

Hi rissicay, potresti spiegare che cosa è la parte "if (index)?" La mia comprensione sta dicendo "if (index == 0) indext = 1;" che equivale a dire "index + = 1;" –

+0

Sì, hai ragione, fatti una modifica: – rissicay

-1

Questo dovrebbe fare quello che vuoi:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<script> 
var spins = 1000000 
var chunkSize = 1000; 
var stats = {a:0,spins:0};    

function doIt() { 
    for (var chunk = 0; chunk < chunkSize; chunk++) { 
     stats.a++; 
    } 
    document.getElementById("result").innerHTML = stats.a; 
    if(++stats.spins < spins) setTimeout(doIt,5); 

} 

</script> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>Untitled Document</title> 
</head> 

<body onload="doIt()"> 
<div id=result>result goes here.</div> 
</body> 
</html> 
1

Devi vicino a un esempio di lavoro nel primo test, ma lo faccio vedere un errore logico. Nel tuo if(), devi tornare dalla funzione altrimenti eseguirà sempre più funzioni in competizione per quel thread.

var process = function() { 
    for (; index < spins; index++) { 
    stats.a++; 
    big = (big/3.6)+ big * 1.3 * big/2.1; 
    console.write(big); 
    // Perform xml processing 
    if (index + 1 < spins && index % 100 == 0) { 

document.getElementById("result").innerHTML = stats.a; 
     setTimeout(process, 5); 
     //!!!!! 
     return;//without this it'll keep iterating through the for loop without waiting for the next 5ms 
     //!!!!! 
    } 
    } 

document.getElementById("result").innerHTML = stats.a; 
}; 

Nessuno dei campioni come-è attendere il successivo setTimeout (nel 2 ° Esempio si continua l'iterazione attraverso il vostro ciclo fino a quando il ciclo for è stato completato, ma ad ogni dimensione di blocco si imposta un timeout per un successivo iterazione .Tutto ciò fa ritardare l'intera sequenza per 5 ms, si accumulano ancora ed eseguono 5 ms da quando il ciclo for inizia iterando)

Tutto sommato, sembra che tu stia andando nella giusta direzione, ci sono solo piccoli errori logici in entrambi gli esempi.

+0

Grazie, questo è stato utile, anche se non capisco cosa sta succedendo. Ho aggiunto questo ritorno, quindi restituisce ogni numero (es. 1,2,3) invece di 100,200 ecc. Ho aggiunto && (indice> 0) al if e un indice aggiuntivo ++ prima del ritorno, e ora sembra funzionare. La cosa strana è che ho copiato questo esempio testualmente da uno dei pochi esempi pubblicati su come fare Suppongo che abbiano pubblicato codice non funzionante –

+0

ya, buon punto, dato che stai iniziando a multipli di 100, l'indice> 0 è necessario per la 1a iterazione per avere successo (come 0% 100 == 0). Ci sono esempi che sono pronti per la copia/incolla e quelli che sono puntatori, forse quello che hai usato era mirato ad essere un puntatore. Ma è un errore comune per cose come le funzioni ricorsive e come in JavaScript, il differito per cicli che si basano su variabili globali come "indice". Oh bene. Quindi sei dove devi essere adesso? La tua app funziona come speravi? – chwagssd

0

C'è una libreria su github disponibile per questo tipo di grande loop senza bloccare il browser/thread e senza usare web-worker.

Un po 'sperimentale e supporta grandi cicli ricorsivi, ma potrebbe funzionare per voi. Dipende da q.js per la risoluzione delle promesse.

https://github.com/sterpe/stackless.js

Problemi correlati