La réponse de Fabrício est parfaite; mais je voulais compléter sa réponse par quelque chose de moins technique, qui se concentre sur une analogie pour aider à expliquer le concept d'asynchronicité .
Une analogie ...
Hier, le travail que je faisais exigeait des informations d'un collègue. Je l'ai appelé; voici comment s'est déroulée la conversation:
Moi : Salut Bob, je dois savoir comment nous foo « d la barre » d la semaine dernière. Jim veut un rapport à ce sujet, et vous êtes le seul à en connaître les détails.
Bob : Bien sûr, mais cela me prendra environ 30 minutes?
Moi : c'est super Bob. Donnez-moi un coup de fil quand vous aurez les informations!
À ce stade, j'ai raccroché le téléphone. Comme j'avais besoin d'informations de Bob pour terminer mon rapport, j'ai quitté le rapport et je suis allé prendre un café à la place, puis j'ai rattrapé un e-mail. 40 minutes plus tard (Bob est lent), Bob a rappelé et m'a donné les informations dont j'avais besoin. À ce stade, j'ai repris mon travail avec mon rapport, car j'avais toutes les informations dont j'avais besoin.
Imaginez si la conversation s'était déroulée comme ça à la place;
Moi : Salut Bob, je dois savoir comment nous foo « d la barre » d la semaine dernière. Jim veut un rapport à ce sujet, et vous êtes le seul à en connaître les détails.
Bob : Bien sûr, mais cela me prendra environ 30 minutes?
Moi : c'est super Bob. J'attendrai.
Et je me suis assis là et j'ai attendu. Et attendu. Et attendu. Pendant 40 minutes. Ne rien faire mais attendre. Finalement, Bob m'a donné l'information, nous avons raccroché et j'ai terminé mon rapport. Mais j'avais perdu 40 minutes de productivité.
Il s'agit d'un comportement asynchrone ou synchrone
C'est exactement ce qui se passe dans tous les exemples de notre question. Charger une image, charger un fichier sur le disque et demander une page via AJAX sont toutes des opérations lentes (dans le contexte de l'informatique moderne).
Plutôt que d' attendre la fin de ces opérations lentes, JavaScript vous permet d'enregistrer une fonction de rappel qui sera exécutée une fois l'opération lente terminée. Dans l'intervalle, cependant, JavaScript continuera d'exécuter d'autres codes. Le fait que JavaScript exécute un autre code en attendant la fin de l'opération lente rend le comportement asynchrone . Si JavaScript avait attendu la fin de l'opération avant d'exécuter tout autre code, cela aurait été un comportement synchrone .
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);
Dans le code ci-dessus, nous demandons à JavaScript de se charger lolcat.png
, ce qui est une opération lente . La fonction de rappel sera exécutée une fois cette opération lente terminée, mais en attendant, JavaScript continuera de traiter les lignes de code suivantes; ie alert(outerScopeVar)
.
C'est pourquoi nous voyons l'alerte apparaître undefined
; puisque le alert()
est traité immédiatement, plutôt qu'après le chargement de l'image.
Afin de corriger notre code, tout ce que nous avons à faire est de déplacer le alert(outerScopeVar)
code dans la fonction de rappel. Par conséquent, nous n'avons plus besoin de la outerScopeVar
variable déclarée comme variable globale.
var img = document.createElement('img');
img.onload = function() {
var localScopeVar = this.width;
alert(localScopeVar);
};
img.src = 'lolcat.png';
Vous verrez toujours qu'un rappel est spécifié en tant que fonction, car c'est le seul * moyen en JavaScript pour définir du code, mais ne l'exécutez que plus tard.
Par conséquent, dans tous nos exemples, le function() { /* Do something */ }
est le rappel; pour corriger tous les exemples, il suffit de déplacer le code qui a besoin de la réponse de l'opération là-dedans!
* Techniquement, vous pouvez également l'utiliser eval()
, mais il eval()
est mauvais à cet effet
Comment faire attendre mon appelant?
Vous pourriez avoir actuellement un code similaire à celui-ci;
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);
Cependant, nous savons maintenant que cela return outerScopeVar
se produit immédiatement; avant que la onload
fonction de rappel n'ait mis à jour la variable. Cela conduit à getWidthOfImage()
revenir undefined
et à undefined
être alerté.
Pour résoudre ce problème, nous devons autoriser la fonction appelante getWidthOfImage()
à enregistrer un rappel, puis déplacer l'alerte de la largeur pour qu'elle soit comprise dans ce rappel;
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);
});
... comme précédemment, notez que nous avons pu supprimer les variables globales (dans ce cas width
).