[Javascript] Perché la mia variabile è inalterata dopo averla modificata all'interno di una funzione? - Riferimento di codice asincrono


Answers

La risposta di Fabrício è azzeccata; ma volevo integrare la sua risposta con qualcosa di meno tecnico, che si concentra su un'analogia per aiutare a spiegare il concetto di asincronismo .

Un'analogia ...

Ieri, il lavoro che stavo facendo richiedeva alcune informazioni da un collega. Gli ho telefonato; ecco come è andata la conversazione:

Io : Ciao Bob, ho bisogno di sapere come abbiamo perso il bar la scorsa settimana. Jim vuole un rapporto su di esso, e tu sei l'unico che conosce i dettagli a riguardo.

Bob : Certo, ma mi ci vorranno circa 30 minuti?

Io : è fantastico Bob. Dammi un anello indietro quando hai le informazioni!

A questo punto, ho riagganciato il telefono. Poiché avevo bisogno di informazioni da parte di Bob per completare il mio rapporto, ho lasciato il rapporto e invece ho preso un caffè, poi ho ricevuto alcune e-mail. 40 minuti dopo (Bob è lento), Bob ha richiamato e mi ha dato le informazioni di cui avevo bisogno. A questo punto, ho ripreso il mio lavoro con la mia relazione, poiché avevo tutte le informazioni di cui avevo bisogno.

Immagina se la conversazione fosse andata in questo modo;

Io : Ciao Bob, ho bisogno di sapere come abbiamo perso il bar la scorsa settimana. Jim vuole un rapporto su di esso, e tu sei l'unico che conosce i dettagli a riguardo.

Bob : Certo, ma mi ci vorranno circa 30 minuti?

Io : è fantastico Bob. Aspetterò.

E mi sono seduto lì e ho aspettato. E aspettato. E aspettato. Per 40 minuti. Non fare altro che aspettare. Alla fine, Bob mi ha dato le informazioni, abbiamo riattaccato e ho completato il mio rapporto. Ma avevo perso 40 minuti di produttività.

Questo è asincrono rispetto al comportamento sincrono

Questo è esattamente ciò che sta accadendo in tutti gli esempi nella nostra domanda. Caricare un'immagine, caricare un file da un disco e richiedere una pagina tramite AJAX sono tutte operazioni lente (nel contesto del computing moderno).

Anziché attendere il completamento di queste operazioni lente, JavaScript consente di registrare una funzione di richiamata che verrà eseguita al termine dell'operazione lenta. Nel frattempo, tuttavia, JavaScript continuerà a eseguire altro codice. Il fatto che JavaScript esegua un altro codice mentre attende il completamento dell'operazione lenta rende il comportamento asincrono . Se JavaScript avesse atteso che l'operazione fosse completata prima di eseguire qualsiasi altro codice, sarebbe stato un comportamento sincrono .

var outerScopeVar;    
var img = document.createElement('img');

// Here we register the callback function.
img.onload = function() {
    // Code within this function will be executed once the image has loaded.
    outerScopeVar = this.width;
};

// But, while the image is loading, JavaScript continues executing, and
// processes the following lines of JavaScript.
img.src = 'lolcat.png';
alert(outerScopeVar);

Nel codice sopra, chiediamo JavaScript per caricare lolcat.png , che è un'operazione sloooow . La funzione di callback verrà eseguita una volta eseguita questa operazione lenta, ma nel frattempo, JavaScript continuerà a elaborare le righe successive del codice; cioè alert(outerScopeVar) .

Questo è il motivo per cui vediamo l'avviso undefined ; poiché l' alert() viene elaborato immediatamente, piuttosto che dopo che l'immagine è stata caricata.

Per correggere il nostro codice, tutto ciò che dobbiamo fare è spostare il codice alert(outerScopeVar) nella funzione di callback. Di conseguenza, non è più necessaria la variabile outerScopeVar dichiarata come variabile globale.

var img = document.createElement('img');

img.onload = function() {
    var localScopeVar = this.width;
    alert(localScopeVar);
};

img.src = 'lolcat.png';

Vedrai sempre che una funzione di callback è specificata come funzione, perché è l'unico * modo in JavaScript per definire un codice, ma non eseguirlo fino a un momento successivo.

Pertanto, in tutti i nostri esempi, function() { /* Do something */ } è il callback; per correggere tutti gli esempi, tutto ciò che dobbiamo fare è spostare il codice che richiede la risposta dell'operazione in quel punto!

* Tecnicamente puoi anche usare eval() , ma eval() è malvagio per questo scopo

Come posso mantenere il mio chiamante in attesa?

Al momento potresti avere un codice simile a questo;

function getWidthOfImage(src) {
    var outerScopeVar;

    var img = document.createElement('img');
    img.onload = function() {
        outerScopeVar = this.width;
    };
    img.src = src;
    return outerScopeVar;
}

var width = getWidthOfImage('lolcat.png');
alert(width);

Tuttavia, ora sappiamo che il return outerScopeVar avviene immediatamente; prima che la funzione di callback onload abbia aggiornato la variabile. Questo porta a getWidthOfImage() restituisce undefined , e undefined essendo alert'd.

Per risolvere questo problema, è necessario consentire alla funzione che chiama getWidthOfImage() di registrare un callback, quindi spostare l'avviso della larghezza in modo che si trovi all'interno di tale callback;

function getWidthOfImage(src, cb) {     
    var img = document.createElement('img');
    img.onload = function() {
        cb(this.width);
    };
    img.src = src;
}

getWidthOfImage('lolcat.png', function (width) {
    alert(width);
});

... come prima, nota che siamo stati in grado di rimuovere le variabili globali (in questo caso la width ).

Question

Dati gli esempi seguenti, perché outerScopeVar è indefinito in tutti i casi?

var outerScopeVar;

var img = document.createElement('img');
img.onload = function() {
    outerScopeVar = this.width;
};
img.src = 'lolcat.png';
alert(outerScopeVar);

var outerScopeVar;
setTimeout(function() {
    outerScopeVar = 'Hello Asynchronous World!';
}, 0);
alert(outerScopeVar);

// Example using some jQuery
var outerScopeVar;
$.post('loldog', function(response) {
    outerScopeVar = response;
});
alert(outerScopeVar);

// Node.js example
var outerScopeVar;
fs.readFile('./catdog.html', function(err, data) {
    outerScopeVar = data;
});
console.log(outerScopeVar);

// with promises
var outerScopeVar;
myPromise.then(function (response) {
    outerScopeVar = response;
});
console.log(outerScopeVar);

// geolocation API
var outerScopeVar;
navigator.geolocation.getCurrentPosition(function (pos) {
    outerScopeVar = pos;
});
console.log(outerScopeVar);

Perché viene emesso undefined in tutti questi esempi? Non voglio soluzioni alternative, voglio sapere perché questo sta accadendo.

Nota: questa è una domanda canonica per l' asincronicità JavaScript . Sentiti libero di migliorare questa domanda e aggiungi altri esempi semplificati con cui la comunità può identificarsi.




In tutti questi scenari outerScopeVar viene modificato o assegnato a un valore in modo asincrono o in un secondo momento (in attesa o in attesa che si verifichi qualche evento), per cui l'esecuzione corrente non attenderà . Così in tutti questi casi i risultati del flusso di esecuzione corrente in outerScopeVar = undefined

Analizziamo ogni esempio (ho contrassegnato la parte chiamata in modo asincrono o ritardata per alcuni eventi):

1.

Qui registriamo un eventlistner che verrà eseguito su quell'evento particolare. Qui il caricamento dell'immagine. Quindi l'esecuzione corrente continua con le righe successive img.src = 'lolcat.png'; e alert(outerScopeVar); nel frattempo l'evento potrebbe non verificarsi. es., funtion img.onload aspetta che l'immagine riferita img.onload caricata, in modo asincrono. Questo succederà a tutti gli esempi seguenti: l'evento potrebbe essere diverso.

2.

Qui l'evento di timeout riproduce il ruolo, che invocherà il gestore dopo il tempo specificato. Qui è 0 , ma continua a registrare un evento asincrono che verrà aggiunto all'ultima posizione della Event Queue per l'esecuzione, il che garantisce il ritardo garantito.

3.

Questa volta, callback ajax.

4.

Il nodo può essere considerato come un re della codifica asincrona. Qui la funzione contrassegnata viene registrata come gestore di richiamata che verrà eseguito dopo aver letto il file specificato.

5.

Una promessa evidente (qualcosa sarà fatto in futuro) è asincrona. vedi Quali sono le differenze tra Differito, Promesso e Futuro in JavaScript?

https://www.quora.com/Whats-the-difference-between-a-promise-and-a-callback-in-Javascript




Per affermare l'ovvio, la coppa rappresenta outerScopeVar .

Le funzioni asincrone sono come ...