FOR asincrono in Javascript

inserito nella categorie: Javascript
marchiato con i tag: , , , , , ,
Asynchronous

Una delle mancanze basilari in Javascript è la presenza di uno snippet di codice che realizzi automaticamente, senza tanti fronzoli, senza setTimeout o setInterval impostate dal programmatore un FOR asincrono.
Quali sono i benefici di un costrutto for asincrono? Beh, innanzitutto il non bloccaggio del thread del browser.. se volete eseguire una funzione N volte senza che il browser “si impalli”. Ovviamente sfruttando bene il costrutto si possono realizzare applicazioni di grande efficienza con pochissimo codice.
Bene, oggi mi son cimentato nel realizzarle uno, e il risultato è senz’altro comodo.. ma vediamo come:

Il tutto è realizzato come una funzione (non potevo fare altrimenti).
Sappiamo tutti come funziona un classico for:
for (INITIAL_EXPRESSION; LOOP_CONDITION; FINAL_EXPRESSION) { CALLBACK; }

Bene, ho voluto ri-realizzare questa sintassi per il mio for asincrono, infatti per usarlo vi basta (con qualche accorgimento) utilizzarlo in questo modo:
afor (INITIAL_EXPRESSION; LOOP_CONDITION; FINAL_EXPRESSION; TIMEOUT; CALLBACK)

Dal momento che sfrutto la funzione eval di Javascript, le prime tre espressioni devono essere inserite tra stringhe, e la quarta in un costrutto function(){}, in questo modo:
afor ('i=0', 'i<10', 'i++', 100, function(){ console.log(i) })

In caso di problemi, non esitate a commentare.

Se hai letto questo articolo, potrebbero interessarti anche:

Commenti (12)

  • Amberly scrive:

    Please teach the rest of these internet hooligans how to write and reaserch!

  • Ok, io volevo solo offrirti alcune considerazioni sulle tue affermazioni, nulla di che.

  • Antonio Trapani scrive:

    Ascolta, non ho tempo da perdere dietro a dettagli. Che il mio codice stampasse solo 100, sebbene fosse così, è assolutamente irrilevante, ho scritto di fretta la prima cosa che mi è passata per la mente. Volevo solo offrirti alcune considerazioni sul tuo ‘for asincrono’, ma credo di dover rinunciare.

    A presto

  • Flavio scrive:

    Spiacente, ma non è cosi.
    Nel tuo esempio (peraltro non funzionante, in quanto ovviamente la variabile i settata come argomento nel document.write() viene presa come è nel ultimo punto di esecuzione del for [ovvero 100]) in ogni ciclo FOR metti una funzione nella coda. Dato che tra un incremento e l’altro passano 1~2ms, tu riempi la coda di funzioni che andranno eseguite.
    hai uno “stack” pieno di funzioni da essere eseguite, che poi saranno eseguite tutte in una volta.
    STACK: “f1 f2 f3 f4 f5 f6 …”;

    Se noti bene la funzione afor, ad ogni completamento della funzione i-esima, viene impostata nella coda la nuova funzione!
    Completo f1, imposto TIMEOUT per f2 … Eseguo f2, imposto TIMEOUT per f3.
    Se qualcosa dovesse andare male per f2, ad esempio un ritardo sincrono, non avrò nella coda f3, in quanto aspetterò il completamento di f2.

    Certo, la controindicazione è usare delle stringhe da valutare con eval(), ma ho creato lo snippet velocemente, e non ho per ora trovato una soluzione migliore.

    A presto.

  • Antonio Trapani scrive:

    Nel tuo esempio, la stessa gestione asincrona può essere scritta così:

    for (var i=0; i++; i<100) {
    setTimeout(function() {
    document.write(i)
    }, 10)
    }

    il codice non è più complesso di quello che hai scritto, il tuo enorme drawback è dover passare gli argomenti come stringhe (il cui eval peraltro inficia le prestazioni).

    Rimangono i presupposti che ho discusso in precedenza. Se la funzione è poco complessa (in termini di tempo) puoi evitare una gestione asincrona, altrimenti dovresti applicare una gestione asincrona all'interno della funzione in modo che ogni singolo frammento di codice non impieghi più di 100ms ad essere eseguito.

  • Flavio scrive:

    Uhm allora, innanzitutto grazie per la chiarezza espositiva.
    Comunque il mio intento era realizzare una funzione che facesse quello che fa una setTimeout con una variabile da incrementare fino ad una condizione, il tutto senza scrivere ogni volta i soliti fronzoli.

    Esempio, metti caso che debba, chessò, scrivere i numeri da uno a 100 in un documento (esempio stupido).
    Bene, il codice da scrivere per realizzare tutto cio’ sarebbe:

    var i = 1;
    var int = setInterval(function(){
    document.write(i+"
    ");
    i++;
    if (i==100) clearInterval(int);
    }, 10);

    Giusto? Bene, invece usando il FOR asincrono avresti una “semplicità della lettura del codice” maggiore, in quanto:

    afor ('i=0', 'i<100', 'i++', 10, function(){
    document.write(i)
    });

    Tutto questo ricorda molto il classico for.

    Se tu volessi fare tutto questo con il classico for, avresti la stessa sintassi, ma sai bene che ti troveresti tutti i numeri da 1 a 100 in una volta sulla pagina web, senza nessun "distacco di tempo" tra loro.

    Spero di essere stato chiaro su cosa volessi creare con questo codice.

  • Antonio Trapani scrive:

    Ciao Flavio,
    anche io penso che sia un approccio sbagliato. Riprendo il punto di vista di Federico per farti capire meglio.
    Premetto che per implementare un orologio basta un setInterval e non serve un for.
    Ad ogni ciclo del tuo for la funzione viene schedulata in coda alla UI Queue che, come sappiamo, è il solo thread di cui disponiamo. Se voglio compiere 10 iterazioni con t=100ms il risultato è che, probabilmente, in 1sec avrai piazzato 10 funzioni in coda alla UI queue. Quanto tempo ci impiega la funzione per essere eseguita? Questo ovviamente dipende dalla funzione, in applicazioni complesse potrebbe impiegarci 500ms o anche più.
    Il problema vero non risiede nel tempo della funzione ma nel numero di funzioni consecutive che hai piazzato nella coda del browser. Se ogni funzione impiega 100ms per essere eseguita, questo significa che in quel secondo nient’altro può essere eseguito. Se ad esempio tu avessi bisogno di visualizzare un output in seguito ad una chiamata AJAX lanciata in precedenza, avresti un ritardo. Se l’utente in quel secondo preme un bottone avvertirebbe un ritardo di 1sec prima di avere un feedback dall’interfaccia.
    Ricorda che un principio da seguire per un bravo web developer è che il tempo di esecuzione di ogni frammento di codice (schedulato nella UI queue) deve durare non più di 100ms, che è il tempo massimo per illudere un utente e simulare un’interfaccia multitask.
    Spero di essere stato chiaro, ti lascio alla lettura di queste slide che credo saranno per te illuminanti: http://www.slideshare.net/nzakas/high-performance-javascript-jquery-conference-sf-bay-area-2010-3843763

    P.S.
    I WebWorker sono uno strumento molto potente. Purtroppo al giorno d’oggi se pensiamo ad applicazioni mainstream non possiamo non supportare browser come IE8 (~12% global users) che non ne dispongono. Sono quindi auspicabili tecniche di fallback come quella che trovi nelle slide.

    Antonio Trapani
    Senior Engineer @ Cascaad/CircleMe

  • Flavio scrive:

    mi dispiace, ma non hai capito il “senso”.
    Metti caso che tu debba creare, chessò, un orologio con precisione a millisecondi.
    Beh, di certo non userai un normale for, in quanto tra un ciclo e l’altro il browser non farà il refresh della pagina, quindi non “vedrai” l’orologio scorrere.
    Se invece usi una setInterval, una setTimeout, il browser fara l’update() dei controlli della pagina e modificherà l’ HTML.
    Altre possibili applicazioni potrebbero essere nell’uso della grafica con i Canvas.

  • Interessante ma secondo me “non va”.

    tempo di esecuzione: t*cicli; se cicli=100 e t=100 ci mette 10 secondi = lento
    il browser si bloccherà comunque i volte se f è davvero così CPU-intensive
    se f prende più tempo di t, le funzioni si ammucchieranno alla fine bloccando il browser fino alla fine del ciclo
    eval causa overhead

    La migliore soluzione al momento sono i web workers e probabilmente il server via ajax per IE≤9

  • Flavio scrive:

    certo che si può fare :)

  • 0cool scrive:

    si puo’ fare la stessa cosa per un while?

Commenta anche tu!