Newsgroup: it.comp.lang.javascript
Titolo: leggere file sequenzialmente

O anche solo: leggere piï¿œ di un file. Non ci riesco.
La funzione importMy legge un file di testo e inserisce tutte le righe 
(o meglio tutti i campi di ciascuna riga) nell'array concLine.
L'array files contiene l'elenco dei files che devo leggere.
Poi c'ï¿œ il loop sugli elementi di files. Ebbene, il loop si interrompe 
una volta terminato di elaborare il primo elemento, e poi restituisce 
"undefined". Insomma, viene letto solo il primo files ma non i 
successivi. Dove sbaglio? Grazie.
 
var concLine=[];
var myLine=[];
 
function importMy(myLine) {
 
	var txtFile = new XMLHttpRequest();
	txtFile.onreadystatechange = function() {
		if (txtFile.readyState == 4) {
			var lines = txtFile.responseText.split("\n");
				for (i=0; i<lines.length; i++) {
     				var a=lines[i].split(",");
					concLine.push([a[0],a[1]],a[2]);
				}
		}
	}
	txtFile.open("GET", myLine, false);
	txtFile.send(null);	
}
 
var files=[
	qui ho l'elenco dei miei file da leggere
]
 
for (i=0;i<files.length;i++) {
         console.log(files[i]);
	importMy(files[i]);
	console.log(concLine.length);
}


 
Grazie anche qui. Io perï¿œ non ottengo mai lo status 200. Forse perchï¿œ 
trattandosi di file in locale (file:/// eccetera) non c'ï¿œ un server che 
restituisce tale valore?

 restituisce tale valore?
 
Per curiositï¿œ, stai scrivendo un'estensione per qualche browser, oppure
il codice ï¿œ anch'esso caricato da una pagina locale? Te lo chiedo perchï¿œ
i browser sono / stanno diventando piï¿œ restrittivi, in merito.
 
Inoltre ho visto che fai una chiamata sicrona all'interno di un ciclo:
ora, sebbene tu stia caricando file locali, le chiamate sincrone non
sono mai consigliate, infatti avevo sentito che volevano deprecarla a
livello di standard, la flag e avere solo chiamate asincrone (dubito lo
faranno, onestamente, soprattutto a breve).
Le chiamate sincrone bloccano il thread in cui vengono eseguite, in
alcuni browser questo vuol dire anche bloccare tutto il browser finchï¿œ
non finisce (in alcuni Sistemi Operativi un tempo voleva dire anche
bloccare pure quelli).
 
Il mio suggerimento, sia per questioni di performance, che di eventuali
problemi futuri, sarebbe di passare a richieste asincrone usando il
sistema delle Promises per gestire il tutto. ï¿œ una soluzione elegante,
che ï¿œ fatta apposta per la casistica in cui ti trovi.
Volendo, se non vuoi usare librerie esterne, puoi implementare a mano il
minimo indispensabile che ti serve.
Considera inoltre, che le versioni recenti di jQuery utilizzano le
Promises, quindi se per caso usi jQuery per altri motivi te le ritrovi
"aggratis".
 
Il mio ï¿œ ovviamente solo un suggerimento spassionato, il tuo codice
indubbiamente funziona anche cosï¿œ e magari non hai tempo o interesse nel
cambiarlo.

 restituisce tale valore?
 
Sï¿œ, certo, ï¿œ per quello: la proprietï¿œ in questione si riferisce allo 
status code del protocollo http.
 
Anche io, come Zer0, sarei curioso di sapere a che stai lavorando.

 che ï¿œ fatta apposta per la casistica in cui ti trovi.
 
Sono ben felice di prendere in considerazione il tuo suggerimento ma 
devo confessare di non avere capito un'acca. Perchï¿œ il mio codice non ï¿œ 
asincrono? Perchï¿œ avrei dovuto usare una funzione callback? E cosa sono 
le Promises? Un aiuto, un link con del codice di esempio per capire 
meglio? Grazie.

 Anche io, come Zer0, sarei curioso di sapere a che stai lavorando.
 
Creazione di report da archivi locali in formato di puro testo. 
Applicazione casalinga senza nessuna pretesa, solo una curiositï¿œ di 
partenza: vedere fino a che punto Javascript puï¿œ sostituire php+mysql, 
cioï¿œ se posso fare a meno di un server e far fare le elaborazioni 
(ordinamenti, conteggi, somme, aggregazioni) al browser del client.

>> che ï¿œ fatta apposta per la casistica in cui ti trovi.
 
[cut]
 
Ho letto il materiale che mi hai indicato. Forse sbaglio, perï¿œ nel mio 
caso mi viene naturale pensare in maniera "sincrona". Se io leggo le 
righe di un file, per sommarle e calcolarne il totale, prima di 
effettuare le operazioni di addizione devo essere sicuro di avere i miei 
addendi. Con richieste asincrone, come farei? Forse ï¿œ il cambio di 
paradigma richiesto, ma proprio non mi ci ritrovo.
 

 paradigma richiesto, ma proprio non mi ci ritrovo.
 
Astraendo la logica, nel tuo caso che è sincrono, potresti avere:
 
    1. per ogni file,
    2.    leggi il contenuto del file,
    3.    inserisci le prime tre righe in un array globale,
    4. stampa le righe totali
 
(So che nel tuo caso la stampa è per ogni file, ma credo anche sia per
una questione di debug, qui è per farti un'esempio)
 
Ora, tu l'operazione "3" e "4" la puoi fare solo perché la "2" è
sincrona. Se fosse asincrona, al punto "3" e "4" non avresti ancora i
dati da elaborare.
 
Nel caso delle promises tu hai una logica di questo tipo:
 
    1. per ogni file leggi il contenuto, POI
    2. inserisci le prime tre righe di ciascuno in un array globale, POI
    3. stampa le righe totali
 
È una semplificazione, ma il punto è il "POI" (`then`): la successiva
operazione la fai solo quando la precedente è stata risolta, anche
essendo asincrona. Le Promises in pratica invece che restituire il
valore della chiamata (che, essendo asincrona, non possono avere subito)
ti restituiscono la "promessa" che ti daranno il valore della chiamata.
Tu puoi concatenare, raggruppare e quant'altro questo genere di
"promesse" in modo da avere un codice totalmente asincrono.
 
E rendono il codice molto leggibile. L'esempio sul sito della Microsoft
è chiarificatore:
 
    var results = searchTwitter(term).then(filterResults);
    displayResults(results);
 
Nota che `results` sarà una promises non il valore effettivo. La
funzione `displayResults` prenderà come parametro una promises a sua
volta, ed eseguirà le operazioni una volta risulta. Lo stesso codice, a
livello di logica, avrebbe potuto essere:
 
    searchTwitter(term)
        .then(filterResults)
        .then(displayResults);
 
Nel tuo caso specifico, ogni chiamata http sarebbe una "promessa".
Potresti avere sia la logica del tipo "appena tutte le 'promesse' sono
state risolte - ergo, hai ricevuto il contenuto di tutti i file -
aggiungi le prime righe di ognuno in un array globale" (e questo è
importante se vuoi mantenere l'array con un ordine prestabilito), oppure
"appena una 'promessa' è risulta - ergo hai ricevuto un contenuto di un
file - mettila nell'array globale" (e questo ti permette di popolare
l'array più velocemente, se ti bastassero anche dati parziali, ma non
hai garanzie sull'ordine in cui il contenuto del file viene restituito).
 
Spero che ti abbia aiutato a chiarire un po' di più.
 
Se ti interessa vedere le potenzialità delle tecnologie web come
applicazioni, ti consiglio di dare un'occhiata a Mozilla Open Web Apps:
https://www.mozilla.org/en-US/apps/
 
Usando l'HTML5 Application Cache, puoi anche fare applicazioni offline,
e con le File APIs puoi leggere il contenuto di file – ma deve essere
sempre l'utente a inserire manualmente il file locale, per questioni di
sicurezza, non puoi accedere in modo programmatico a un file qualunque
sul computer senza interazione utente.
 

          .then(displayResults);
 
Non sono ancora sicuro di avere capito bene.
In sostanza le funzioni argomento di then vengono eseguite nel momento 
in cui le promises sono risolte, ma intanto l'esecuzione del codice 
procede comunque oltre le righe di then.
Nel mio caso la lettura dei dati è operazione propedeutica a qualunque 
altra operazione, e mi pare che dovrei impedire qualunque richiesta 
dell'utente fino a quando essa non è terminata. Cioè dovrei fare 
qualcosa del tipo:
 
       leggifile("miofile.dat")
           .then(rendivisibilipulsanti_ordina_totale)
 
Giusto o c'è qualcosa di molto più semplice che mi sfugge?

 .then(rendivisibilipulsanti_ordina_totale)
 
Cioè, non vorrei risultare troppo criptico.
Ammettiamo che a me serva avere una maschera con due soli pulsanti che 
consentano di ordinare le righe di un array e calcolare il totale delle 
righe dello stesso array.
Chiaramente non ha senso ordinare l'array quando ancora non è stato 
completamente acquisito. Come non ha senso, almeno così mi pare, 
permettere all'utente di richiamare il comando di ordinamento senza che 
questo possa venire eseguito subito perché, come prima, ancora l'array 
non è stato caricato.
Ecco perché a me è venuto in mente di nascondere i pulsanti fintanto che 
la lettura del file non è stata completata.
 
 

           .then(rendivisibilipulsanti_ordina_totale)
 
Il vantaggio delle promises è il fatto puoi raggrupparle. Se hai una
lista di file, hai bisogno di una lista di promises. Mettiamo caso che
hai un file solo da caricare, quello che farai sarà:
 
    1. passare a stato di "loading" (animazione, finestra con pulsante
di cancella, inibizione dell'interfaccia non necessaria)
 
    2. leggere il file
 
    3. elaborare il file
 
    4. passare a stato di "interazione" (nascondere l'animazione di
loading, riabilitare interfaccia)
 
In pseudo javascript sarebbe:
 
    // Un oggetto `App` che rappresenta l'applicazione principale
    // Si presuppone che il `setLoadingStatus` mostri solo una banale
    // animazione
    App.setLoadingStatus();
 
    readFile("miofile.dat")
        .then(parseResults)
        .then(App.setNormalStatus);
 
 

 la lettura del file non è stata completata.
 
Si certo. Più che nascondere, andrebbero disabilitati, oppure – come si
fa spesso su Web – usare un layer opaco che ne impedisce l'interazione
(almeno con il mouse, in realtà per fare le cose a modino bisognerebbe
tener di conto anche della tastiera).
 

 lista di file, hai bisogno di una lista di promises.
 
Ecco, tutto il resto è chiaro. Con più file da leggere, come posso 
procedere, in pseudocodice?
Può andare quello che scrivo sotto?
 
var completo=0;
for (var i=0 to elencofile.length-1;i++) {
    leggifile(elencofile[i]).then(controllafineoperazione)
}
 
function controllafineoperazione() {
completo++;
if (completo==elencofiles.length) {
  ... e qui scrivo il codice per abilitare i pulsanti
}
}

 }
 
Scusate la fretta. E grazie per tutte le spiegazioni.

 Può andare quello che scrivo sotto?
 
Non proprio. Le promises servono appunto per evitarti quei vari
"completo" di cui sotto. Direi che sarebbe più qualcosa del genere:
 
 
    var promisesFile = [];
 
    for (var i = 0; i < elencofile.length; i++) {
        // readFile restituisce una promises
        promisesFile.push(readFile(elencofile[i]));
    }
 
    when(promisesFile)
        .then(doSomethingWithFileContents)
        .then(displayButtons);
 
Nota che la parte `when(promisesFile)` dipende dal tipo di
implementazione di promises che usi. In alcuni casi `when` non accetta
array, e hai bisogno di cose tipo `when.all(promisesFile)`, in altri si.
 
Comunque sia, a livello di concetto funziona così: non hai bisogno di
controllare manualmente quando tutte le promises sono risolte (tramite
variabili come `completo`), perché ci pensano da sole. Hai una lista di
promises e dici "quando tutte sono risolte, fai questo".
 
 
 

 Scusate la fretta. E grazie per tutte le spiegazioni.
 
Ci mancherebbe, e non preoccuparti. Il NG serve appunto a questo. :)
 

      displayResults(results);
 
Mi sono arenato. Ho cercato di adattare l'esempio al caso della lettura 
di un file ma mi sono accorto che non so come definire Promise e .then 
(e forse ho definito .resolve e .reject alla membro di segugio).
Inoltre... dove ricevo il contenuto del file, cioè il valore restituito 
da leggiFile()?
 
var Promise = function () {
// che metto qui dentro?
};
 
Promise.prototype.then = function (onResolved, onRejected) {
// che metto qui dentro?
};
 
Promise.prototype.resolve = function (value) {
return value;
};
 
Promise.prototype.reject = function (error) {
return "errore";
};
 
function leggiFile() {
 
var xhr, results, promise;
 
// path è opportunamente inizializzato
url = path+"lista.txt";
promise = new Promise();
xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
 
xhr.onreadystatechange = function() {
	if (txtFile.readyState == 4) {
         promise.resolve(this.responseText);
     }
};
 
xhr.onerror = function (e) {
promise.reject(e);
};
 
xhr.send();
return promise;
 
}
 
function alertAAA() {alert("AAA")}
 
leggiFile().then(alertAAA);


 
Hope it helps!