Newsgroup: it.comp.lang.javascript
Titolo: domanda su semplice esempio ajax con funzione callback

Ciao,
sono incappato in un comportamento che non mi so spiegare.
Posto il sorgente dell'esempio:
 
 
(pagina "index.html")
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "
http://www.w3.org/TR/html4/loose.dtd
">
<html>
   <head>
      <title>Test Javascript</title>
      <script type="text/javascript" src="test.js"></script>
   </head>
   <body>
 
      <button type="button" onclick="myFunction();">Change Content</button>
 
      <div id="myDiv">
         <h2>Let AJAX change this text</h2>
      </div>
 
   </body>
</html>
 
 
(file "test.js")
 
function myFunction()
{
   var temp = null;
 
   function loadMyData(request)
   {
      temp = request.responseText;
   }
 
   makeRequest('ajax_info.txt', loadMyData);
 
   function ProcessData()
   {
      window.alert("in ProcessData()\n");   \\ !!!
 
      document.getElementById("myDiv").innerHTML = temp;
   }
 
   ProcessData();
}
 
 
function makeRequest(url, callback)
{
   var xmlHttpRequestObj;
 
   if (window.XMLHttpRequest)
      xmlHttpRequestObj = new XMLHttpRequest();
   else
      xmlHttpRequestObj = new ActiveXObject("Microsoft.XMLHTTP");
 
   try
   {
      xmlHttpRequestObj.onreadystatechange = function() {
      if (xmlHttpRequestObj.readyState == 4 && xmlHttpRequestObj.status
== 200)
         callback(xmlHttpRequestObj);
      };
 
      xmlHttpRequestObj.open("GET", url, true);
      xmlHttpRequestObj.send();
   }
   catch (err)
   {
      window.alert("Impossibile contattare il server o risorsa non
trovata.");
   }
}
 
 
(il file "ajax_info.txt" ï¿œ presente nella stessa dir e contiene
questo semplice testo:
 
AJAX is not a new programming language.<br>
<br>
AJAX is a technique for creating fast and dynamic web pages.<br>)
 
 
Eseguendo l'esempio il browser mi emette il popup "in ProcessData()"
e stampa correttamente il contenuto del file richiamato.
Se perï¿œ commento l'istruzione
 
window.alert("in ProcessData()\n");
 
non viene stampato nulla, e sembra che non succeda nulla.
Da cosa puï¿œ dipendere?
 

 }
 
FORSE, e dico FORSE, l'Alert blocca l'esecuzione della riga seguente, ma 
non la funzione asincrona richiamata da AJAX, che quindi ha il tempo di 
valorizzare la variabile temp a un valore che non sia null.
 
Ma in generale è un codice che sta su come un castello di carte, come hai 
notato.
 
Dovresti studiarti un po' il concetto di funzioni asincrone in Javascript.
 
Bye.


 
Metti
document.getElementById("myDiv").innerHTML = temp;
dentro a
function loadMyData(request)
 
Come ti ha detto Alessandro hai scritto una cosa che è estremamente 
fragile proprio da un punto di vista architetturale
 
Se devi solo caricare testi in maniera asincrona può anche andare bene, 
ma se devi fare un'interfaccia interattiva iniziando così ti trovi nella 
cpsa marrone in brevissimo tempo.
 
Inizia a pensare tutto in termini di callback. È molto più complesso 
finché non entri nell'ottica, ma è l'unico modo
 
 
Ci sei relativamente vicino perché la loadMyData è appunto una callback
Se rinomini ProcessData in warnRemoteDataProcessingHasStarted() rendi il 
codice più comprensibile dato che in quella funzione no è vero che 
processi dei dati
 
Un refactoring potrebbe essere:
 
  function displayResponseData(response)
  {
     document.getElementById("myDiv").innerHTML = response.responseText;
  }
 
  function warnProcessHasStarted()
  {
     window.alert("Processo iniziato");
  }
  function myFunction()
  {
      makeRequest('ajax_info.txt', displayResponseData);
      warnProcessHasStarted();
  }
 
Un ulteriore refactoring sarebbe togliere l'argomento a 
displayResponseData, assegnando il valore del testo ad una variabile 
direttamente da dentro la makeRequest, prima di richiamare la callback 
che di tutta la risposta non sa bene che farsene
 
-- 
I fatti mi cosano

 Bye.
 
grazie, ciao.
 

 che di tutta la risposta non sa bene che farsene
 
qui non ho capito bene cosa vuoi dire
 
 

 a riguardo hai qualche link di materiale specifico da segnalarmi?
 
Io ho letto qualche settimana fa "Async Javascript" (trovato su Leanpub) 
che è una buona introduzione ai vari sistemi di programmazione asincrona, 
anche se non li approfondisce molto.
 
Ti consiglierei Professional Javascript For Web Developers (di Zakas, 
uscito per la Wrox) che è uno dei migliori manuali che abbia letto (di 
qualsiasi argomento) e copre praticamente tutto.
 
Bye.


Una ottima prassi è avere funzioni senza argomenti, una buona prassi è 
avere funzioni con un solo argomento, una prassi mediocre è avere 
funzioni con due o più argomenti
 
Se passi molti argomenti ad una funzione la probabilità è che quella 
funzione faccia molte cose, il che è *male*
 
In particolare se passi un argomento ciccione finisce che crei 
dipendenza fra la funzione e il punto in cui viene chiamata, il che 
serve solo a nascondere bug
 
Se hai una funzione che fa molte cose, spezzala in funzioni più piccole 
che facciano meno cose ciascuna, possibilmente una cosa soltanto ciascuna.
Facendo così vedrai che inizierai a trovarti delle funzioni comode da 
chiamare e non ti ripeterai.


 Una ottima prassi è avere funzioni senza argomenti,
 
Forse intendi metodi? Altrimenti mi rimane difficile immaginare come una
funzione senza argomenti possa essere un'ottima prassi – nel qual caso,
forse mi sfugge qualcosa.


var qualcosa = 0;
var oggetto = {}:
oggetto.incrementaQualcosa = function(){
qualcosa++;
};
 
In Javascript non vedo una grande differenza fra metodi e funzioni, sbaglio?
 
Ammetto che le funzioni nulladiche hanno lasciato perplesso anche me, ma 
sicuramente quelle moandiche sono da preferire al proliferare di argomenti
Con buona pace di cose come l'sdk di android che ha spesso metodi con 
firme che arrivano a 6 argomenti (roba da diventarci matti)
 
Sono un po' in berserk sul fare codice pulito causa lettura di questo libro:
http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882
 
 
Effettivamente molte cose sono sacrosante


 sicuramente quelle moandiche sono da preferire al proliferare di argomenti
 
Sono perfettamente d'accordo che se una funzione, o metodo, prende
troppi argomenti, è indice di cattivo design. Ma tra "troppi" e
"nessuno" c'è una bella differenza. E mi chiedevo come "nessuno" possa
essere considerato un'ottima pratica.
 

 };
 
dimmi che stai scherzando...
 
-- 
ZeD


Non mi prendo mai troppo sul serio, ma stavolta non mi sembrava di 
scherzare.
A te invece perché sembra di sì?



Confesso che in Javascript sono appena sopra al livello scarpa.
Tu come cambieresti il mio esempio?


 A te invece perché sembra di sì?
 
Per, buona parte, i motivi che ti ho esposto nel post precedente suppongo.
Prima di tutto, le variabili globali. Amo sempre riportare questa
metafora, quando si parla di variabili globali. Lo scope globale è un
po' come un cesso pubblico: non lo usi, a meno che tu non sia veramente
costretto; e anche quando lo fai cerchi di toccarlo il meno possibile.
 
Secondo, una funzione – in questo caso addirittura un metodo di un
oggetto – il cui stato è esterno. Che già di per sé, appunto, fa rizzare
i capelli in testa (e, suppongo, sia stato questo principalmente il
motore della reazione di ZeD), e che a livello tecnico porta almeno due
grandi problemi:
 
1. L'oggetto, e il metodo, dipendono da uno stato nello scope esterno.
Significa che il comportamento del metodo non è predicibile, perché
varia in base allo scope. Qualunque altra funzione potrebbe,
inavvertitamente, modificare `qualcosa`, generando un effetto a cascata
di comportamenti errati. Di norma, si preferisce incapsulare questo
genere di cose.
 
2. L'oggetto, e il metodo, dipendono da uno stato nello scope esterno.
Significa che la funzione / oggetto non è atomica, e non è possibile
esportarla né riutilizzarla, perché ha una forte dipendenza con lo scope
in cui è definita.
 
Questi, su due piedi, i motivi principali a mio avviso. Che poi possono
portare ovviamente a tutta un'altra serie di considerazioni, io ho
giusto espresso quelle piú ovvie secondo me.


cut...
Mi sa che ho scelto un esempio infelice.
Proviamo con un caso realistico di come scrivo queste cose e se hai 
abbastanza pazienza dammi un consiglio (o puntami verso un buon manuale)
 
Premetto che io in javascript scrivo molto poco e probabilmente sono 
ancora un po' naïf
 
//Siamo dentro a nodejs + nowjs quindi ho un oggetto this.now creato 
dalla libreria
 
var elencoElementi = [];
 
everyone.now.dammiIlMioElemento = function(callback){
   for(var id in elencoElementi){
     if(elencoElementi[id].uid == this.now.uid){
	callback(elencoElementi[id].uid);


 Tu come cambieresti il mio esempio?
 
Te l'ho già mostrato nel post precedente, come cambierei il tuo esempio.
Volendo andare un po' oltre, potrei usare l'incapsulamento:
 
    var oggetto = (function() {
        var count = 0;
 
        return {
            inc: function() {
                count++;
            },
            getCount: function() {
                return count;
            }
        }
    })();
 
    oggetto.inc();
 
    console.log(oggetto.getCount());
 
In questo modo non è neanche possibile per altre funzioni accedere
esplicitamente a `count` - come invece poteva essere possibile
nell'esempio che ti ho fatto nel post precedente – e quindi non possono
modificarne lo stato interno, ma solo recuperarlo – `getCount`.
 
Personalmente, comunque, io non userei un oggetto. Userei una funzione.
Qualcosa tipo:
 
    function inc(a) {
        return a + 1;
    }
 
Questo risolve tutti i problemi esposti precedentemente, e lo fa in modo
elegante. Una volta John Carmack
(
http://it.wikipedia.org/wiki/John_Carmack
 nel caso qualcuno ignori chi
sia) twittò:
 
"Sometimes, the elegant implementation is just a function. Not a method.
Not a class. Not a framework. Just a function".
 
La funzione non dipende da alcuno stato esterno, prende un dato in input
e ne restituisce un altro in output. Nel codice quindi avrai, banalmente:
 
    var qualcosa = 0;
 
    qualcosa = inc(qualcosa);
 
Laddove questo può sembrarti poco sensato – potresti dire, perché non
usare un semplice ++? – avere una funzione ti aiuta a sfruttare la parte
di programmazione funzionale di JavaScript. Un esempio? Mettiamo che hai
un array di numeri, e vuoi incrementarne i valori. Normalmente potresti
fare:
 
    var array = [1, 3, 8];
 
    for (var i = 0; i < array.length; i++)
        array[i]++
 
Avendo `inc`, invece:
 
    var mynewarray = array.map(inc);
 
Il vantaggio inoltre è che `map` ritorna un array nuovo. Il che
significa che non altera lo stato di originale di `array`, che è un
bene. Volendo ovviamente, potresti sempre avere:
 
    array = array.map(inc);
 
Ora arriva la parte bella. Metti caso che non solo vuoi incrementare, ma
vuoi anche ottenere solo i numeri pari, una volta incrementato.
Usando sempre l'approccio di cui sopra, ti basta creare una funzione `even`:
 
    function even(a) {
        return a % 2 === 0
    }
 
    e chiamarla con `filter` semplicemente nell'array restituito da map:
 
 
    var myevenarray = array.map(inc).filter(even);
 
Il vantaggio è visibile. Non solo puoi cambiare facilmente l'ordine –
prima filtri per i numeri pari, poi incrementi - ma `inc` e `even` sono
funzioni generiche, che puoi usare in qualsiasi contesto:
 
    if (even(myNumber)) {
        // do something
    }
 
I metodi `map` e `filter`, sono funzioni di ordine superiore (meglio
note come "high-order function" in informatica). Una high-order function
prende un'altra funzione come parametro, e/o restituisce un'altra
funzione come risultato. Vedasi:
http://en.wikipedia.org/wiki/Higher-order_function
 
 
Di cose da dire ce ne sarebbero tante, ma credo che di carne al fuoco ce
ne sia già tanta.

 abbastanza pazienza dammi un consiglio (o puntami verso un buon manuale)
 
La domanda sorge spontanea. Esempio realistico di cosa? Mi spiego
meglio: questa parte di thread nasce dal mio dubbio di come avere una
funzione, soprattutto che non sia metodo, possa essere considerata
un'ottima prassi se non prende alcun argomento.
 
Ora, nell'esempio sottostante tu hai un metodo (dell'oggetto `now`), che
prende un argomento (`callback`), la quale `callback`, che è una
funzione, prende a suona volta un argomento.
 
Nessuna parte di quel codice quindi "spiega" come mai una funzione senza
argomenti debba essere considerata un'ottima prassi, giacché non ve ne è
neanche una.
 
Quindi immagino che l'esempio sia relativo a qualcos'altro, che magari
mi son perso per strada?
 


Scusa, mi sono perso io parti di contesto
Il fatto che le funzioni nulladiche siano il santo graal viene fuori 
dalla lettura di "Clean Code" di Robert C. Martin
È uno dei punti che ha lasciato più perplesso anche me, pur condividendo 
di sicuro l'opinione che due o più argomenti siano pratica da evitare 
ove possibile
Diciamo che tendo a fidarmi data la bontà di quasi tutti gli altri 
consigli che ho trovato in quel libro finora
Detto questo finisco anch'io ad usare spesso almeno un argomento
 
La domanda e l'esempio nuovo erano relativi all'obiezione che mi hai 
sollevato riguardo usare oggetti in uno scope esterno alla funzione
 
Apprezzerei molto un consiglio su come riscrivere quel pezzo di codice
Al momento da quanto leggo qui dalle tue risposte la sai lunga, o 
comunque moooolto più lunga di quanto la sappia io in Javascript (e ci 
vuole poco)



Due risposte: quanto sono ignorante e quanto è facile prendere 
sottogamba javascript
 
Dato che nei prossimi mesi mi toccherà usare nodejs un bel po' di più 
sarà meglio che mi metta a studiare più seriamente



grazie delle segnalazioni.
Ciao
 

 chiamare e non ti ripeterai.
 
Sisi, conosco la decomposizione funzionale e so quanto � utile ;-)
 
 

 ha scritto:

Forse quello a cui si alludeva erano le vecchie "procedure"
(nel gergo comune a pi� linguaggi) o "routine" (pi� specifico
nel gergo assembly), cio� un gruppo di istruzioni identificate
da un nome e che non ha alcun bisogno di parametri per funzionare
(ai tempi del basic per DOS, per esempio, era prassi comune scrivere
una procedura che stampava il men� dell'applicazione,
un'altra che puliva lo schermo, ecc.).
 

 sottogamba javascript
 
Infatti Javascript � molto, ma molto pi� complesso di quanto
possa apparire... Soprattutto poi la sua riscoperta � stata pi�
pompata da quando � stato ripreso agli onori della cronaca grazie
alla "moda" AJAX (non che prima non fosse utile Javascript, anzi,
ma in molti lo consideravano linguaggio giocattolo per farci effetti
particolari su pagine web).
 
 
 

 Forse quello a cui si alludeva erano le vecchie "procedure"
 
Dubito che il libro in questione parli di linguaggi procedurali, dato
che è recente. È piú probabile che si riferisca a come scrivere codice
pulito in un'ottica di OOP.
 
E comunque anche ai tempi del basic le procedure prendevano argomenti; e
il poterle scrivere senza – cosí come puoi fare adesso – non rendeva
tale pratica "consigliata".


 
5. Usare le promises, soprattutto per via del punto 3, in questo caso se
l'elemento non viene trovato si può fare una rejected.
 
6. Disaccoppiare qualunque cosa sia attaccata a `everyone.now` con la
ricerca dell'elemento in sé, dato che la variabili `elencoElementi` sta
al di fuori di `dammiIlMioElemento` e non è parte dello stesso oggetto
(`everyone.now`), ma è usata dallo stesso. O fai diventare tale
variabile parte dello stesso oggetto – ma ignoro le complicazioni che si
avrebbero su nowjs – oppure `dammiIlMioElemento` semplicemente accede a
una funzione o a un metodo di un oggetto che restituisce l'elemento
interessato, magari passando come parametro `this.now.uid`.
In questo modo la funzione `dammiIlMioElemento` non accede direttamente
a `elencoElementi`.
 
C'è anche da dire che tu stai lavorando con nodejs, quindi non hai
problemi di "scope globale", perché puoi – e dovresti – usare moduli il
piú possibile per isolare il codice e incapsulare i dati.