Newsgroup: it.comp.lang.javascript
Titolo: AJAX query molto ravvicinate

Ciao a tutti, sono alle prese con un applicazione AJAX per interagire
con un database mysql.
Funziona tutto benone, o almeno sembre, il problemone è che con la
struttura che utilizzo ora non posso fare query multiple, e vi spiego
il perchè:
//Creo l'oggetto XmlHttp per fare le richieste al database
function CreateXmlHttpObject(myFunction){
  var xmlhttpObject=null;
  try{
    xmlhttpObject=new XMLHttpRequest();
  }catch(e){
    try{
        xmlhttpObject=new ActiveXObject("Msxml2.XMLHTTP");
    }catch(e){
        xmlhttpObject=new ActiveXObject("Microsoft.XMLHTTP");
    }
  }
  xmlhttpObject.onreadystatechange=myFunction;
  return xmlhttpObject;
}
//Leggere il valore di ritorno dal database
function readResponseText(){
    if(myRequest.readyState==4&&myRequest.status==200){
        readedData=myRequest.responseText;
}
//Invoca la query SELECT sul database
function readFromDB(data){
	myRequest=CreateXmlHttpObject(readResponseText);
        myRequest.open("POST",web);
	myRequest.setRequestHeader("content-type",
"application/x-www-form-urlencoded");
	myRequest.send("data="+data);
}
Ecco come potete vedere io lancio la quey la quale mi ritorna il valore
sulla funzione readResponseText().
I problemi che sorgno sono due:
1) Come faccio ad utilizzare questo valore?
2) Se faccio due query in tempi brevi, come faccio a sapere a quale
query appartiene il dato letto?
Siccome le query che devo fare sono davvero tante, e diverse(il dato
che leggono lo devo utilizzare in modo diverso), come faccio a
processare il dato di ritorno?
Non posso scrivere una funzione che mi risolva la query per ogni query,
non finirei più...
Esiste una modalità sincrona per tutto ciò? in modo che io possa fare
una cosa del tipo:
var datoRitornato=lanciaQuery();
e che blocchi il flusso del codice fino a quando non mi ritorni un dato.

 struttura che utilizzo ora non posso fare query multiple
Ma perchè non butti nel cesso Ajax ed usi invece la coppia Perl -
Javascript che il problema te lo risolve?

 e che blocchi il flusso del codice fino a quando non mi ritorni un dato.
In teoria si può fare (perché XMLHttpRequest può anche operare in modo
sincrono), ma è assolutamente da evitare.
Quello che dovresti usare è un sistema che esegua più richieste
(sequenzialmente o parallelamente, ma sempre in modo asincrono) e invochi
una tua funzione quando tutte sono completate.
Sono certo che i vari framework Ajax che ci sono in giro hanno già
funzionalità del genere, ma se ti piace scriverti tutto a mano da solo,
posso riciclare un pezzo di codice che ho scritto un po' di tempo fa quando
comiciavo a pasticciare con il JS. Di seguito spiego come si usa; in fondo
al messaggio trovi la definizione di 'XHRequests'. (Nota che è codice
puramente "dimostrativo" e privo di gestione degli errori. Inoltre, usa GET
e non POST. Aggiustalo e adattalo, se fa al caso tuo. Provato solo con
Firefox e Opera.)
 function XHR_creator() { return new XMLHttpRequest() ; }
 // al posto di XHR_creator ci metti una tua funzione che instanzi un XHR 
 // in qualsiasi browser
 var r = new XHRequests(XHR_creator); 
 r.addRequest("req1", "/myurl", { "data" : "valore1"   }  ) ;
 r.addRequest("req2", "/myurl", { "data" : "valore2"   }  ) ;
 // il primo arg è il nome simbolico della richiesta, il secondo è l'URL,
 // il terzo sono i vari parametri che finiranno nella query string
 function my_completion_func(req)
 {      
        alert("my_completion_func: " + 
                 req.getResponse("req1").status +  " / " +
                 req.getResponse("req2").status );
 
        var req1txt = req.getResponse("req1").text;
         // volendo, c'è anche l'attributo .xml
 }
 // 'activate' fa partire tutte le richieste...
 r.activate(my_completion_func);
 // quando saranno tutte completate verrà invocata 'my_completion_func'
Max
/* ---------- definizione di XHRequests -----*/
function XHRequests(xmlhttpfactory)
{
        this.running = 0;
        this.completed = 0;
        this.request = { };
        this.addRequest = function(name,url,data)
        {
                this.request[name] = 
                {       url : url, data: data, response : null, 
                        xhr : xmlhttpfactory() 
                };
        };
 
        this.getResponse = function(name)
        {       return this.request[name].response;
        };
        var activateRequest = function(owner,name,completion)
        {
                        var request = owner.request[name];
                        var data = request.data;
                        var url = request.url;
                        var hr =  request.xhr;
                        hr.onreadystatechange  = function()
                        {
                         if( hr.readyState==4 )
                         {
                                ++owner.completed;
                                if( hr.status == 200 )
                                {
                                        owner.request[name].response = { 
                                                text : hr.responseText,
                                                xml : hr.responseXML,
                                                status : 200    };
                                }
                                else
                                {
                                        owner.request[name].response = 
                                        {       text : null, xml :  null, 
                                                status :  hr.status     };
                                }
                                if( owner.running == owner.completed )
                                {
                                        completion(owner);
                                }
                         }
                        };
                        url += "?";
                        for( var i in data )
                        {
                                url += encodeURIComponent(i) 
                                 + "=" + encodeURIComponent( data[i] ) + "&";
                        }
                        hr.open("GET", url, true );
                        hr.send(null);
                        ++owner.running;
        };
        this.activate = function(completion)
        {
                if( this.running == 0 )
                {
                  for( var name in this.request )
                  {
                        activateRequest(this,name,completion);
                  }
                }
        };
 
}

 wrote:

Mai utilizzati in coppia, please illuminami magari con qualche esempio o
letteratura in rete (non voglio evitare google ma evitare di perdermi).
La cosa mi interessa proprio come alternativa ad Ajax.
Grazie
Ciao
-- 
Virgilio

 il perchè:
[CUT]
se una ha bisogno del valore dell'altra perché non le usi sincrone?
-- 
"Ho smesso di fumare.
 Vivrò una settimana di più.
 E in quella settimana pioverà a dirotto"
									    W.A.

 
...cut..
ciao
-- 
Virgilio

 Ciao
Giusto, l'ho sparata lì e poi sono scappato.
Perl è presente ed ha un forte supporto nella maggior parte dei server
in circolazione. Uno dei supporti che rende Perl interessante è
cpan.org
 che mette a disposizione una miriade di moduli completi di
sorgente per risolvere una quantità enorme di problemi.
Difficile non trovare il modulo giusto. Molti moduli sono preistallati
nei server, Altri si possono istallare se necessario. L'istallazione
non richiede difficili procedure.
Una ricerca rapida mi da come risultato il modulo:
http://search.cpan.org/dist/Net-MySQL/MySQL.pm
che ha come caratteristica:
"From perl you activate the interface with the statement  use
Net::MySQL; After that you can connect to multiple MySQL daemon and
send multiple queries to any of them via a simple object oriented
interface."
tra le funzioni si nota:
"The each() method takes out the reference result of one line at a
time, and the return value is ARRAY reference."
Se ho capito bene disponi quindi di un array di queries.
Cercando in rete ho trovato anche questo script Perl:
ftp://ftp.genome.ou.edu/pub/programs/blast2many
Che ha, fra le altre, la seguente caratteristica
"$::my_name - program to split a Blast output file from multiple
queries
into separate output files in a directory, one output file for each
query result."
Naturalmente si può adattare per avere le queries in memoria invece
che su file separati.
Javascript è solo su lato client, dunque ti può servire per le
costruzioni di interfaccia su quel lato. La gestione delle queries è
tutto lavoro Perl.
Spero ti sia utile.
Ciao
Oreste

 Oreste
Ciao
-- 
Virgilio

 Perche' assolutamente?
Beh, forse ho fatto un'affermazione un po' perentoria, però onestamente non
vedo casi in cui sia molto sensato fare richieste sincrone.
Il vantaggio della modalità asincrona (oltre a quello di poter gestire più
di una richiesta contemporaneamente) è quello di mantenere il controllo
dell'interfaccia e decidere se e quando interrompere una richiesta in corso
(magari interpellando preventivamente l'utente).
In modalità sincrona sei completamente in balia dell'XHR e non recuperi il
controllo se non quando la richiesta è terminata. Salvo casi particolari in
cui hai garanzie di efficienza e affidabilità di rete e server, in generale
una richiesta HTTP potrebbe richiedere un tempo arbitrariamente lungo.
Durante questo tempo, il resto dell'applicazione (eventi, intervalli,
timeout) rimane congelato. (Mi pare, inoltre, che con FF e IE6, persino
l'interfaccia del browser resti congelata.)
Max

Max M. ha scritto:

Il fatto è che le query non le posso fare tutte assieme(sempre se ho
capito bene il tuo discorso) in quanto l'applicazione è una GUI con la
quale l'utente fa varie cose, e quindi è l'utente stesso che lancia le
query, e quindi non so a priori cosa lanciare.
Il problema sorge proprio quando l'utente fa due richiesta molto
ravvicinate.

Cristiano Larghi ha scritto:

Grosso modo era il succo del discorso :D
Come faccio a farle sincrone? Per lo meno quelle che più
frequentemente vengono lanciate ravvicinate.

 frequentemente vengono lanciate ravvicinate.
c'è un parametro del metodo Open dell'oggetto xmlhttp che indica se fare
una chiamata sincrona...
-- 
"qualcuno è andato per età
 qualcuno perchè già dottore
 e insegue una maturità, si è sposato, fa carriera
 ed è una morte un po' peggiore"
									    F.G.

 ravvicinate.
OK, avevo un po' frainteso. Pensavo che nel constesto della stessa
operazione dovessi fare più richieste e aspettare che ciascuna fosse
completata prima di poter proseguire.
In questo caso, mi pare che le problematiche siano due: 1) decidere se vuoi
che le varie richieste vengano eseguite contemporaneamente, 2) distinguere
una richiesta dall'altra dentro la funzione di callback.
Per quanto riguarda (1), se vuoi che le richieste vengano eseguite assieme
devi usare oggetti XHR separati (come già fai); diversamente devi
costruirti un qualche tipo di coda e attivare ogni richiesta quanto quella
precedente è completata. Per quanto riguarda (2), se aggangi a
'onreadystatechange' la stessa identica funzione, potrai distinguere le
varie query soltanto in base a qualche dato che trovi nella risposta. Se,
però, per ogni richiesta contruisci una closure distinta, puoi incapsulare
le informazioni che desideri nella funzione stessa e ritrovartele quanto
gestisci l'evento di completamento della richiesta. Ad esempio, se nel tuo
caso il tipo di query è rappresentato dal valore di 'data', potresti (a)
aggiungere un argomento 'data' a 'readResponseText' e (b) dentro
'readFromDB', anziché passare semplicemente 'readResponseText' a
'CreateXmlHttpObject', passare una funzione anonima di questo tipo:
  myRequest = CreateXmlHttpObject( function() { readResponseText(data); } );
In questo modo, dentro 'readResponseText' saprai sempre di quale richiesta
ti stai occupando in quel momento.
Max

 qualche problema di stabilità con i browser KHTML (Safari e Konqueror).
Nel mio caso il fatto di essere bloccanti serve(applicazioni su reti
locali) invece per problemi KHTML non sono a conoscenza e prendo nota. 
-- 
Virgilio

Max M. ha scritto:

Ok grazie mille!
Direi che così possa andare.
Per rendere il codice più leggebile avevo inserito nella funzione
readResponsText() l'esecuzione di un'altra funzione.
In pratica:
function readResponText(){
     if(bla bla bla ){
           myFunction();
     }
}
In questo modo potevo lanciare una query così:
function lunchQuery(){
        myFunction=function{
                        if(readedData=="qualcosa"){
                                  //fai qualcosa
                        }
        }
        readFromDb();
}
Ecco cosi ogni funzione che mi lancia una query ingloba già nella
chiamata quello che deve essere eseguito dentro la funzione
readResponsText().
Il problema però qui era che se faccio due chiamate, x così dire
"parallele" si fa a sovrascrivere la funzione myFunction(), mentre se
le chiamate sono in cascata questo non succede.
Direi però che usare il metodo da te suggeritomi sia meglio.
Grazie

 wrote:

Confermo che mi e' stato utilie. Per il momento ho provato ad utilizzare
Perl per alcune cose di poco conto ed in via _sperimentale_ cpan e' una
miniera o meglio un pozzo senza fondo. Appena capita l'occasione di
utilizzare ajax mi dirotto su perl e poi posto qualcosa. Molto
interessante lo script suggerito :-)
Ciao e grazie di nuovo 
-- 
Virgilio

 interessante lo script suggerito :-)
Ti segnalo l'ng specifico:
it.comp.lang.perl
Ciao
Oreste

 wrote:

Non ho mai postato sul iclp pero' sono un lurkatore assiduo... non
manchero' ovviamente appena utilizzero' Perl in maniera seria adesso la
richiesta dei clienti e' rivolta a php...
ciao
-- 
Virgilio

 Senza postarne, ma Perl per ajax lo uso normalmente... vedi faqintosh :)
Frequento faqintosh e conosco il tuo _debole_ per Perl :) avevo notato
l'uso di ajax ma sinceramento non ho indagato.
Per postare intendevo dire se ho qualche problema o dubbi da
sottoporre... magari sul ng preposto
Ciao
-- 
Virgilio