+ Rispondi al messaggio
Visualizzazione dei risultati da 1 a 5 su 5

passare oggetto json da callback a funzione chiamante

  1. #1
    BennyB non è in linea Scolaretto
    Buongiorno.
    Sto imparando JS, e dopo un po' di esercizi risolti sono incappato in un problema che a me pare incredibile.
    Allora, ho scritto una callback che carica un oggetto JSON da una libreria pubblica.
    function loadComuni(){
      var xhr = new XMLHttpRequest();
      xhr.open('GET','https://raw.githubusercontent.com/matteocontrini/comuni-json/master/comuni.json', true);
    
      xhr.onload = function(){
        if (this.status == 200) {
          var datiJson = JSON.parse(this.responseText);
          console.log(datiJson[0].nome, datiJson[0].sigla, datiJson[0].cap, datiJson[0].codiceCatastale);
          return datiJson;
        } else {
          document.alert('Errore di caricamento del DB dei comuni');
        }
      }
      xhr.send();
    }
    
    Fin qui tutto bene: il log mostra il corretto risultato.
    Il mio problema consiste nel fatto che io voglio utilizzare i valori dell'oggetto (ora non solo JSON, ma già JS, dopo il .parse) in una function chiamante, come nell'esempio:
    function carica(){
    var comuni = loadComuni();
    console.log(comuni.length);
    //[...]
    }
    
    Ottengo sempre che 'comuni' è undefined. Ho provato diverse soluzioni, ma l'errore è sempre lo stesso.
    Mi pare incredibile che l'oggetti rimanga "intrappolato" nella callback...
    Dove sbaglio?
    Qualcuno sa spiegarmi i passaggi da fare?

    Grazie.

  2. #2
    L'avatar di _alka_
    _alka_ non è in linea Very Important Person
    Quote Originariamente inviato da BennyB Visualizza il messaggio
    Allora, ho scritto una callback che carica un oggetto JSON da una libreria pubblica. [...]
    Il mio problema consiste nel fatto che io voglio utilizzare i valori dell'oggetto (ora non solo JSON, ma già JS, dopo il .parse) in una function chiamante [...]
    Mi pare incredibile che l'oggetti rimanga "intrappolato" nella callback... [...]
    Più che incredibile, se ci pensi è del tutto normale ciò che sta avvenendo.

    La funzione che hai scritto configura e avvia il download di dati dall'indirizzo che hai specificato.

    L'operazione di download ha inizio quando chiami xhr.send(): nel momento della chiamata, in modo asincrono e in background il browser comincia a svolgere le operazioni necessarie a scaricare i dati, ma il flusso del programma non rimane in attesa e fermo sulla chiamata a xhr.send(), ma prosegue il suo percorso ovvero esce dalla funzione in quanto il corpo della stessa non prevede altre istruzioni (ma se tu aggiungessi un console.log() in quel punto te ne accorgeresti).

    Si esce quindi dalla funzione loadComuni() e, non essendo presente alcun return nel suo corpo, il valore di ritorno risulta undefined, così come riscontri dall'istruzione console.log(comuni.length).

    Nel frattempo, l'operazione di download dei dati è ancora in corso e non è detto che sia terminata!

    Proprio per questo motivo si va a definire una funzione di "callback": l'operazione avviene in modo asincrono e, nel momento in cui ha qualcosa da comunicare, richiama la tua funzione (quella associata a xhr.onload) per segnalare l'avanzare del processo ed eventualmente quando necessario la sua conclusione.

    Quando recuperi quindi i dati JSON scaricati nella funzione di callback, il resto del codice è già stato eseguito e terminato; inoltre, il return di datiJson determina il valore di ritorno della funzione di callback, e non della funzione che la dichiara, ossia loadComuni.

    In sintesi, quando sviluppi con JavaScript, l'approccio asincrono deve essere adottato in ogni contesto: non puoi cioè aspettarti di restituire un valore che avrai a disposizione solo in seguito all'interno di una funzione che avvia operazioni in background ed esce prima che esse siano terminate.

    Quote Originariamente inviato da BennyB Visualizza il messaggio
    Qualcuno sa spiegarmi i passaggi da fare?
    Modifica il codice in modo da adottare un pattern asincrono: invece di usare il valore di ritorno della funzione, passa tu una funzione di callback da invocare quando sono disponibili i dati, e in quella funzione scrivi quello che devi fare quando i dati sono stati scaricati.

    Un esempio di codice (non verificato):
    function loadComuni(afterLoadCallback){
      var xhr = new XMLHttpRequest();
      xhr.open('GET','https://raw.githubusercontent.com/matteocontrini/comuni-json/master/comuni.json', true);
      xhr.onload = function(){
        if (this.status == 200) {
          var datiJson = JSON.parse(this.responseText);
          // Invoco il callback che gestisce i dati una volta ricevuti.
          afterLoadCallback(datiJson);
        } else {
          alert('Errore di caricamento del DB dei comuni');
        }
      }
      xhr.send();
    }
    
    function carica() {
      // Chiamo la funzione loadComuni() e passo il callback da eseguire quando ho ricevuto i dati.
      loadComuni(function (comuni) {
        // Stampo informazioni sui comuni.
        console.log(comuni.length);
        console.log(datiJson[0].nome, datiJson[0].sigla, datiJson[0].cap, datiJson[0].codiceCatastale);
      });
    }
    
    Approfondisci anche gli strumenti disponibili in JavaScript che puoi sfruttare per semplificare questo tipo di programmazione, come ad esempio le Promise.

    Ciao!
    ℹ️ Leggi di più su _alka_ ...

  3. #3
    BennyB non è in linea Scolaretto
    Grazie della risposta.
    Si sarà capito che sono un neofita... in effetti, il concetto di asincrono avrebbe dovuto mettermi sull'avviso.
    Per me sarebbe interessante capire con qualche esempio più approfondito (vale a dire qualche sito o altro dove studiare la faccenda) le implicazioni reali. Se sai indirizzarmi, mi faresti un favore (va da sé anche siti in Inglese). Al momento ho trovato solo materiale per principianti (quale sono, certo), ma vorrei affrontare esempi più complessi e professionali.
    Per studio sto realizzando un primo sito in cui introdurrò esercizi, pagine di riferimento e analisi per HTML e CSS, eccetera.
    In questo caso, si tratta di una pagina per il calcolo del codice fiscale. Volendo appunto studiare, ho realizzato un input in un form con una procedura d'auto completamento del testo scritto, che preleva i dati da una libreria esterna pubblica.
    Ho risolto il mio problema, oggetto del 3D, spostando l'intera procedura nella callback, seguendo il tuo consiglio di ragionare per pattern asincrono. La tua soluzione era efficace, ma eccessivamente complessa rispetto all'ovvietà di lavorare nel punto in cui si certifica l'avvenuto caricamento dei dati. Questo il nuovo codice nella callback:
    function loadComuni(){
      var xhr = new XMLHttpRequest();
      // open (type, url, async)
      xhr.open('GET','https://raw.githubusercontent.com/matteocontrini/comuni-json/master/comuni.json', true);
      xhr.onload = function(){
        if (this.status == 200) {
          var comuni = JSON.parse(this.responseText);
          for (var i in comuni) {
            dati[i]=comuni[i].nome;
          }
          //inizializza la funzione 'autocomplete' e passa l'array
          // console.log(comuni[0].nome, comuni[0].sigla, comuni[0].cap, comuni[0].codiceCatastale);
          autocomplete(document.getElementById("city"), dati);
    
        } else {
          document.alert('Errore di caricamento del DB dei comuni');
        }
      }
      xhr.send();
    }
    
    Chiaramente, funziona bene.
    Mi rimane un piccolo dubbio: nella function autocomplete ci sono alcune altre function, una delle quali prevede un addEventListener con il 'click' o il 'return' dell'utente. In quel momento, io dovrei salvare da qualche parte i dati della posizione x relativa alla scelta utente prelevati dall'oggetto Json. Pensavo di dichiarare un array global in cui salvarli (sono 5 dati in tutto). Il ragionamento fila, o pensi ci siano soluzioni più adatte e/o standard? Credi che porli nella session Storage abbia un senso?

    Riguardo alle Promise le ho analizzate,e faranno parte di qualche esercizio futuro. Sono un po' tentennante sull'uso perché so essere state introdotto con ES6, quindi non tutti i browser le accettano (per non dire delle function async), ma certo le proverò.

    Intanto, grazie ancora.

  4. #4
    L'avatar di _alka_
    _alka_ non è in linea Very Important Person
    Quote Originariamente inviato da BennyB Visualizza il messaggio
    Se sai indirizzarmi, mi faresti un favore (va da sé anche siti in Inglese).
    Non saprei... fai una ricerca mirata con Google.

    Quote Originariamente inviato da BennyB Visualizza il messaggio
    La tua soluzione era efficace, ma eccessivamente complessa rispetto all'ovvietà di lavorare nel punto in cui si certifica l'avvenuto caricamento dei dati.
    La soluzione era un esempio per far capire il meccanismo.
    A parte questo, posso garantire che è tutto fuorché complessa: ci sono costrutti e pattern ben più complicati da affrontare, soprattutto per chi sviluppa abitualmente per il frontend.

    Quote Originariamente inviato da BennyB Visualizza il messaggio
    Mi rimane un piccolo dubbio: nella function autocomplete ci sono alcune altre function, una delle quali prevede un addEventListener con il 'click' o il 'return' dell'utente. In quel momento, io dovrei salvare da qualche parte i dati della posizione x relativa alla scelta utente prelevati dall'oggetto Json. Pensavo di dichiarare un array global in cui salvarli (sono 5 dati in tutto). Il ragionamento fila, o pensi ci siano soluzioni più adatte e/o standard? Credi che porli nella session Storage abbia un senso?
    Non ho idea dell'obiettivo generale dell'applicazione o di cosa fa nello specifico quella funzione autocomplete(), quindi non saprei dire. In generale, non vedo l'utilità di salvare le informazioni in sessionStorage, a meno di non dover navigare in altre pagine che hanno bisogno di quel dato.

    Quote Originariamente inviato da BennyB Visualizza il messaggio
    Riguardo alle Promise le ho analizzate,e faranno parte di qualche esercizio futuro. Sono un po' tentennante sull'uso perché so essere state introdotto con ES6, quindi non tutti i browser le accettano (per non dire delle function async), ma certo le proverò.
    Le Promise sono implementate come "polyfill" all'interno della maggior parte delle librerie, quindi esistono implementazioni alla portata disponibili per qualsiasi browser e qualsiasi versione degli stessi.

    Ciao!
    ℹ️ Leggi di più su _alka_ ...

  5. #5
    BennyB non è in linea Scolaretto
    Ciao.
    Grazie ancora per la risposta.

    Riguardo al punto della tua soluzione, mi sono espresso male: sì, non è complessa, bensì complica un po' il meccanismo logico, che prevede di lavorare nella routine della call-back, come ho fatto.
    Sono certo, invece, che le attività di programmazione front-end sono molto più complesse. Qualcosa ho fatto, sempre per esercizio, e mi sono spinto in routine per me impegnative; non avendo un feed-back esperto che mi corregga o suggerisca, vado un po' a tentoni, e forse creo costrutti più lunghi e ridondanti del necessario, ma col tempo...

    Ho provato a modificare con una Promise, ed è un'alternativa che in effetti facilita il lavoro. Sto studiando...

    Per quanto concerne la sessionStorage, ho risolto banalmente con un array global.

    Grazie ancora per la pazienza.

+ Rispondi al messaggio

Potrebbero interessarti anche ...

  1. Passare slash da variabile php a json
    Da Bivio nel forum PHP
    Risposte: 2
    Ultimo Post: 09-08-2017, 09:30
  2. VB 2010 - Identificare l'oggetto chiamante in un evento
    Da imbranato nel forum Visual Basic .Net
    Risposte: 3
    Ultimo Post: 17-12-2012, 10:44
  3. Function per calcolo - generalizzare oggetto chiamante
    Da liodevac nel forum Microsoft Access
    Risposte: 5
    Ultimo Post: 02-05-2012, 20:54
  4. VB 2010 callback funzione DLL c++ 6.0
    Da mattew75 nel forum Visual Basic .Net
    Risposte: 5
    Ultimo Post: 22-09-2010, 17:16
  5. Risposte: 5
    Ultimo Post: 23-12-2008, 19:14