[Risolto] Javascript: loop con closure e aggiornamento DOM

Linguaggi di programmazione: php, perl, python, C, bash e tutti gli altri.
Avatar utente
keltron
Prode Principiante
Messaggi: 58
Iscrizione: venerdì 2 novembre 2007, 10:49
Contatti:

[Risolto] Javascript: loop con closure e aggiornamento DOM

Messaggio da keltron »

Salve a tutti,
ho un loop nel quale utilizzo una closure con setTimeout per svolgere una sequenza di chiamate ajax, nello specifico la struttura risulta essere la seguente:

Codice: Seleziona tutto

for ( var i = start; i <= stop; i++ ) {
            ( function(x, c, n, d, s){
              setTimeout(function(){
                  $('#loadingNumber').html('test di ** ' + c + ' ** numero** ' + x + ' **');
                  console.log('test di ** ' + c + ' ** numero** ' + n + ' **');
                  checkPath(c, numPath, n, d);
                  loop++;
                  }, 0)
            })(i, contenuto, number, div, stop); 
            console.log('fine loop'); 
}
la funzione checkPath svolge una serie di controlli sui parametri che gli passo, per poi formulare un'opportuna chiamata ajax. Utilizzando questo metodo, le chiamate vengono effettuate in successione e utilizzando i parametri adeguati, nonostante il loop risulti già terminato(merito della closure).
In totale passano alcuni secondi, pertanto con l'utilizzo di JQuery ho pensato di utilizzare il box #loadingNumber per annunciare il tipo di operazione che sarà controllata con checkPath.

Ora il mio problema è il seguente:
quando parte ciascun setTimeout nella console di google chrome vedo effettivamente il messaggio dovuto al blocco di codice

Codice: Seleziona tutto

                  console.log('test di ** ' + c + ' ** numero** ' + n + ' **');
mentre l'aggiornamento di #loadingNumber avviene solamente al termine di checkPath, o per meglio dire della funzione eseguita da setTimeout.
In sostanza il codice

Codice: Seleziona tutto

                 $('#loadingNumber').html('test di ** ' + c + ' ** numero** ' + n + ' **');
viene eseguito soltanto alla fine, mentre avrei come necessità che venga eseguito praticamente subito.
Specifico che ho provato a inserire quest'ultimo codice prima della closure, ma ovviamente nel box appare il messaggio con solamente i valori di fine loop.

Qualcuno saprebbe darmi un consiglio?
Ultima modifica di keltron il martedì 28 ottobre 2014, 15:26, modificato 1 volta in totale.
"There are no stupid questions, but only stupid answers"
Avatar utente
Zoff
Moderatore Globale
Moderatore Globale
Messaggi: 33338
Iscrizione: mercoledì 10 ottobre 2007, 22:36

Re: Javascript: loop con closure e aggiornamento DOM

Messaggio da Zoff »

Ma non puoi metterlo prima della setTimeout() all'interno del ciclo?

PS: Non credo che il codice sia eseguito al termine della clouser, piuttosto il browser aggiorna l'html solo in quel momento. Questo potrebbe essere dovuto all'utilizzo "intensivo" del processo da parte di checkPath()
Prima di aprire una discussione leggi le Guide, poi vedi se c'è un HowTo nel Wiki e fai una ricerca nel Forum!
Applica semplicemente il [Risolto]! Prova: http://forum.ubuntu-it.org/viewtopic.php?f=70&t=548821
Vuoi qualcosa di piu' dal forum? Prova i miei script: http://forum.ubuntu-it.org/viewtopic.php?f=70&t=597066
Avatar utente
keltron
Prode Principiante
Messaggi: 58
Iscrizione: venerdì 2 novembre 2007, 10:49
Contatti:

Re: Javascript: loop con closure e aggiornamento DOM

Messaggio da keltron »

Se mettessi fuori dal setTimeout la parte di codice di JQuery, facendo ad esempio stampare la variabile i sulla quale itero il ciclo, ad esempio

Codice: Seleziona tutto

$('#loadingNumber').html('test numero: '+i);
allora ipotizzando di terminare il loop con i=11, di fatto in #loadingNumber vedrei scritto "test numero: 11" perche' il ciclo termina quasi immediatamente.
L'esecuzione dei setTimeout parte quando ormai il ciclo è già terminato.

Comunque pensandoci bene concordo con te: il mio probabilmente non è un problema di esecuzione, ma di aggiornamento del codice html da parte del browser.
Infatti usando la console di chrome e visualizzando l'html dell'elemento #loadingNumber, questo effettivamente viene aggiornato immediatamente; solo in un secondo momento viene aggiornata la schermata concreta del browser.
"There are no stupid questions, but only stupid answers"
Avatar utente
Zoff
Moderatore Globale
Moderatore Globale
Messaggi: 33338
Iscrizione: mercoledì 10 ottobre 2007, 22:36

Re: Javascript: loop con closure e aggiornamento DOM

Messaggio da Zoff »

keltron [url=http://forum.ubuntu-it.org/viewtopic.php?p=4670698#p4670698][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto:Se mettessi fuori dal setTimeout la parte di codice di JQuery, facendo ad esempio stampare la variabile i sulla quale itero il ciclo, ad esempio

Codice: Seleziona tutto

$('#loadingNumber').html('test numero: '+i);
allora ipotizzando di terminare il loop con i=11, di fatto in #loadingNumber vedrei scritto "test numero: 11" perche' il ciclo termina quasi immediatamente.
L'esecuzione dei setTimeout parte quando ormai il ciclo è già terminato.
Non ti seguo. Stai utilizzando setTimeout passando come timeout 0, quindi viene comunque richiamato immediatamente.
Prima di aprire una discussione leggi le Guide, poi vedi se c'è un HowTo nel Wiki e fai una ricerca nel Forum!
Applica semplicemente il [Risolto]! Prova: http://forum.ubuntu-it.org/viewtopic.php?f=70&t=548821
Vuoi qualcosa di piu' dal forum? Prova i miei script: http://forum.ubuntu-it.org/viewtopic.php?f=70&t=597066
Avatar utente
keltron
Prode Principiante
Messaggi: 58
Iscrizione: venerdì 2 novembre 2007, 10:49
Contatti:

Re: Javascript: loop con closure e aggiornamento DOM

Messaggio da keltron »

Il fatto è che l'aggiornamento di #loadingNumber fuori da setTimeout segue i tempi del ciclo, mentre i tempi di esecuzione di ciasun setTimeout sono ben più lunghi.
Per meglio capirci ho creato un esempio funzionante e testabile (l'ho provato con wampserver, utilizzando chrome come browser).

Pagina index.php (contenente anche il codice javascript)

Codice: Seleziona tutto

<html>
<head>

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<script type="text/javascript">

$(document).on("ready", function(){

	$('form').on("submit", function(e){
		e.preventDefault();
		
		// libero i box
		$( "div[id^='action']" ).empty( );
		$( "div[id^='response']" ).empty( );
		
		// raccolgo il numero con cui decidero' il tipo di loop
		id=	$(this).attr('id');
		n = id.match(/\d+$/)[0];

		// mio esempio
		if(n==1){
			for ( var i = 0; i < 5; i++ ) {
	            ( function(x){
	              setTimeout(function(){
	                  $('#action'+n).html('Test sub'+n+' numero ** ' + x + ' **');
	                  console.log('Test sub'+n+' numero ** ' + x + ' **');
	                  checkPath(x, n);
	                  }, 0)
	            })(i); 
			}
		}
		// questo esegue le chiamate in maniera sincrona.
		// L'aggiornamento di "respnse" e "action" avviene solamente alla fine di ciascun setTimeout

		// esempio senza setTimeout
		if(n==2){
			for ( var i = 0; i < 5; i++ ) {
	            ( function(x){
	                  $('#action'+n).html('Test sub'+n+' numero ** ' + x + ' **');
	                  console.log('Test sub'+n+' numero ** ' + x + ' **');
	                  checkPath(x, n);
	            })(i); 
			}	
		}
		// questo esegue le chiamate in maniera sincrona, ma l'aggiornamento di "respnse" e "action" avviene solamente alla fine
		 
		// esempio con scrittura fuori dal setTimeout
		if(n==3){
			for ( var i = 0; i < 5; i++ ) {
	            ( function(x){
	              $('#action'+n).html('Test sub'+n+' numero ** ' + x + ' **');
	              setTimeout(function(){
	                  console.log('Test sub'+n+' numero ** ' + x + ' **');
	                  checkPath(x, n);
	                  }, 0)
	            })(i); 
			}
		}
		// questo esegue le chiamate in maniera sincrona.
		// L'aggiornamento di "response" avviene solamente alla fine di ciascun setTimeout	
		// L'aggiornamento di "action" avviene "istantaneamente" seguendo i ritmi del loop, non delle istanze di setTimeout
	    
	    console.log('fine loop'); 
	
	});//end on-submit

});// end on-ready

function checkPath(i, n){
	console.log("\t eseguo chiamata ajax con parametro "+i);
	$.ajax({
	    url : "support.php",
	    type: "GET",
	    data : 'url='+i,
	    async: false,
	    beforeSend:function(){
	    	//console.log('ora provo ajax checkPath');
	        //*$('#titolone').append('checkPath provo '+array[key]);
	    },
	    success:function(data){
	    	$('#response'+n).append(data);
	    },
	    error:function(){
	    	alert('errore!');
	    }
	  }); // end ajax
}

</script>
<style>
	body{background-color:steelblue;}
	form{float:left; width: 25%;}
	div{margin:10px;}
</style>
</head>

<body>

<h1> Clicca per eseguire <i>loop</i>:</h1>
<br/>

<?php
$vec = array(
			"1" => "closure - setTimeout",
			"2"	=> "closure - no setTimeout",
			"3" => "closure - setTimeout - scrittura di action fuori dal setTimeout"
			);
foreach ($vec as $key => $value) {
echo "<form id=\"loop$key\">
<input type=\"submit\" name=\"loop1\" value=\"Sub$key: $value\" />
<div id=\"action$key\"></div>
<div id=\"response$key\"></div>
</form>";
}
?>

</body>
</html>
la pagina support.php sulla quale eseguo la chiamata ajax svolge una serie di operazioni che impiegano circa 5 secondi per effettuare tutto lo script. Per simulare questo fatto ho semplicemente aggiunto sleep(5):

Codice: Seleziona tutto

<?php

sleep(5);

if(isset($_GET['url'])){
	echo 'Rispondo con '.$_GET['url'].'</br>';
}
Il fatto è il seguente: cliccando su Sub1 (che rappresenta l'esempio del mio primo post) , dovrei visualizzare Test sub1 numero ** 4 ** e l'ultimo rispondo dovrebbe essere Rispondo con 3. Invece il numero del "Test" e quello di "Rispondo" sono sempre uguali.
Come già detto da te, credo proprio che il problema sia dovuto all'aggiornamento del browser. Infatti utilizzando gli strumenti da sviluppatore di chrome, effettivamente la scritta dovuta a

Codice: Seleziona tutto

console.log('Test sub'+n+' numero ** ' + x + ' **');
Rispecchia la procedura ideale.
"There are no stupid questions, but only stupid answers"
Avatar utente
Zoff
Moderatore Globale
Moderatore Globale
Messaggi: 33338
Iscrizione: mercoledì 10 ottobre 2007, 22:36

Re: Javascript: loop con closure e aggiornamento DOM

Messaggio da Zoff »

keltron [url=http://forum.ubuntu-it.org/viewtopic.php?p=4671074#p4671074][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto:Il fatto è che l'aggiornamento di #loadingNumber fuori da setTimeout segue i tempi del ciclo, mentre i tempi di esecuzione di ciasun setTimeout sono ben più lunghi.
Continuo a non capire. L'aggiornamento è la prima cosa fatta da setTimeout() quindi non è minimamente influenzato dalla sua lunghezza.
L'unica differenza nel metterlo nel ciclo è che ogni aggiornamento avviene dopo l'aggiornamento precedente mentre se lo si mette nel setTimeout non è garantita la conservazione dell'ordine in cui vengono eseguiti, ma ognuno esegue comunque prima del "proprio" setTimeout.
Prima di aprire una discussione leggi le Guide, poi vedi se c'è un HowTo nel Wiki e fai una ricerca nel Forum!
Applica semplicemente il [Risolto]! Prova: http://forum.ubuntu-it.org/viewtopic.php?f=70&t=548821
Vuoi qualcosa di piu' dal forum? Prova i miei script: http://forum.ubuntu-it.org/viewtopic.php?f=70&t=597066
Avatar utente
keltron
Prode Principiante
Messaggi: 58
Iscrizione: venerdì 2 novembre 2007, 10:49
Contatti:

Re: Javascript: loop con closure e aggiornamento DOM

Messaggio da keltron »

Concordo con quanto da te affermato.
L'aggiornamento è la prima cosa fatta da setTimeout() quindi non è minimamente influenzato dalla sua lunghezza.
Di fatto l'aggiornamento dentro a setTimeout() avviene subito, come mostrato dalla console degli strumenti da sviluppatore. È l'aggiornamento nella pagina viva del Browser che invece avviene a termine di setTimeout(), ed è proprio questo il fatto messo in evidenza cliccando Sub1 nella pagina che ho inserito come test nell'ultimo post.

Aggiungo che ho provato a eseguire il test con altri Browser, e ho scoperto che (almeno su Windows 7) l'esecuzione di questo script è strettamente legata al browser utilizzato.
Nello specifico:
  • Firefox e Opera: funziona esattamente come volevo (e come te avevi sostenuto).
  • Chrome e Safari: persiste il problema sopra esposto.
  • Internet Explorer: come sempre ha un comportamento tutto suo.
A questo punto data la stretta dipendenza dal browser, credo che questo cavillo non sia risolvibile con uno script che funzioni nella medesima maniera in tutti i browser.

Concludo pertanto mettendo [RISOLTO]. Come sempre ti ringrazio della grande disponibilità che offri per trovare la soluzione.

Have a nice day.
"There are no stupid questions, but only stupid answers"
Scrivi risposta

Ritorna a “Programmazione”

Chi c’è in linea

Visualizzano questa sezione: 0 utenti iscritti e 13 ospiti