Recherche…
Exemples d'utilisation de rappel simple
Les rappels offrent un moyen d'étendre les fonctionnalités d'une fonction (ou d'une méthode) sans changer son code. Cette approche est souvent utilisée dans les modules (bibliothèques / plugins), dont le code n'est pas censé être modifié.
Supposons que nous ayons écrit la fonction suivante, en calculant la somme d'un tableau de valeurs donné:
function foo(array) {
var sum = 0;
for (var i = 0; i < array.length; i++) {
sum += array[i];
}
return sum;
}
Supposons maintenant que nous voulons faire quelque chose avec chaque valeur du tableau, par exemple en l’affichant avec alert()
. Nous pourrions apporter les modifications appropriées dans le code de foo
, comme ceci:
function foo(array) {
var sum = 0;
for (var i = 0; i < array.length; i++) {
alert(array[i]);
sum += array[i];
}
return sum;
}
Mais que se passe-t-il si nous décidons d’utiliser console.log
au lieu de alert()
? Évidemment, changer le code de foo
, chaque fois que nous décidons de faire autre chose avec chaque valeur, n'est pas une bonne idée. Il est préférable d'avoir la possibilité de changer d'avis sans changer le code de foo
. C'est exactement le cas d'utilisation des rappels. Il suffit de modifier légèrement la signature et le corps de foo
:
function foo(array, callback) {
var sum = 0;
for (var i = 0; i < array.length; i++) {
callback(array[i]);
sum += array[i];
}
return sum;
}
Et maintenant, nous pouvons changer le comportement de foo
en changeant simplement ses paramètres:
var array = [];
foo(array, alert);
foo(array, function (x) {
console.log(x);
});
Exemples avec des fonctions asynchrones
Dans jQuery, la $.getJSON()
pour récupérer les données JSON est asynchrone. Par conséquent, le passage de code dans un rappel garantit que le code est appelé après la récupération du fichier JSON.
$.getJSON()
syntaxe:
$.getJSON( url, dataObject, successCallback );
Exemple de $.getJSON()
:
$.getJSON("foo.json", {}, function(data) {
// data handling code
});
Ce qui suit ne fonctionnerait pas , car le code de traitement des données serait probablement appelé avant que les données ne soient réellement reçues, car la fonction $.getJSON
prend un temps indéterminé et ne retient pas la pile des appels en attente du JSON.
$.getJSON("foo.json", {});
// data handling code
Un autre exemple de fonction asynchrone est la fonction animate()
de jQuery. Comme il faut un certain temps pour exécuter l'animation, il est parfois souhaitable d'exécuter du code directement après l'animation.
.animate()
syntaxe:
jQueryElement.animate( properties, duration, callback );
Par exemple, pour créer une animation de fondu après laquelle l'élément disparaît complètement, le code suivant peut être exécuté. Notez l'utilisation du rappel.
elem.animate( { opacity: 0 }, 5000, function() {
elem.hide();
} );
Cela permet à l'élément d'être caché juste après l'exécution de la fonction. Cela diffère de:
elem.animate( { opacity: 0 }, 5000 );
elem.hide();
parce que ce dernier n'attend pas la fin de animate()
(une fonction asynchrone) et que, par conséquent, l'élément est masqué immédiatement, produisant un effet indésirable.
Qu'est-ce qu'un rappel?
Ceci est un appel de fonction normal:
console.log("Hello World!");
Lorsque vous appelez une fonction normale, elle fait son travail et renvoie le contrôle à l'appelant.
Cependant, une fonction doit parfois retourner le contrôle à l'appelant pour effectuer son travail:
[1,2,3].map(function double(x) {
return 2 * x;
});
Dans l'exemple ci-dessus, la fonction double
est un rappel pour la map
fonction car:
- La fonction
double
est donnée à lamap
fonction par l'appelant. - La fonction
map
doit appeler la fonctiondouble
zéro ou plusieurs fois pour effectuer son travail.
Ainsi, la fonction map
renvoie essentiellement le contrôle à l'appelant chaque fois qu'il appelle la fonction double
. Par conséquent, le nom "callback".
Les fonctions peuvent accepter plusieurs rappels:
promise.then(function onFulfilled(value) {
console.log("Fulfilled with value " + value);
}, function onRejected(reason) {
console.log("Rejected with reason " + reason);
});
Ici, la fonction accepte then
deux fonctions de rappel, onFulfilled
et onRejected
. De plus, seule une de ces deux fonctions de rappel est appelée.
Ce qui est plus intéressant, c’est que la fonction retourne then
avant que l’un ou l’autre des callbacks soit appelé. Par conséquent, une fonction de rappel peut être appelée même après le retour de la fonction d'origine.
Continuation (synchrone et asynchrone)
Les rappels peuvent être utilisés pour fournir du code à exécuter après la fin d'une méthode:
/** * @arg {Function} then continuation callback */ function doSomething(then) { console.log('Doing something'); then(); } // Do something, then execute callback to log 'done' doSomething(function () { console.log('Done'); }); console.log('Doing something else'); // Outputs: // "Doing something" // "Done" // "Doing something else"
La méthode doSomething()
ci-dessus s'exécute de manière synchrone avec les blocs callback-execution jusqu'à doSomething()
que doSomething()
renvoyé, garantissant que le rappel est exécuté avant que l'interpréteur ne se déplace.
Les rappels peuvent également être utilisés pour exécuter du code de manière asynchrone:
doSomethingAsync(then) { setTimeout(then, 1000); console.log('Doing something asynchronously'); } doSomethingAsync(function() { console.log('Done'); }); console.log('Doing something else'); // Outputs: // "Doing something asynchronously" // "Doing something else" // "Done"
Les rappels then
sont considérés comme des continuations des méthodes doSomething()
. L' appel de rappel comme dernière instruction d'une fonction est appelé un appel de queue , optimisé par les interprètes ES2015 .
Gestion des erreurs et branchement des flux de contrôle
Les rappels sont souvent utilisés pour gérer les erreurs. C'est une forme de branchement de flux de contrôle, où certaines instructions ne sont exécutées que lorsqu'une erreur se produit:
const expected = true;
function compare(actual, success, failure) {
if (actual === expected) {
success();
} else {
failure();
}
}
function onSuccess() {
console.log('Value was expected');
}
function onFailure() {
console.log('Value was unexpected/exceptional');
}
compare(true, onSuccess, onFailure);
compare(false, onSuccess, onFailure);
// Outputs:
// "Value was expected"
// "Value was unexpected/exceptional"
L'exécution de code dans compare()
ci-dessus a deux branches possibles: success
lorsque les valeurs attendues et les valeurs réelles sont identiques, et error
lorsqu'elles sont différentes. Ceci est particulièrement utile lorsque le flux de contrôle doit être branché après une instruction asynchrone:
function compareAsync(actual, success, failure) {
setTimeout(function () {
compare(actual, success, failure)
}, 1000);
}
compareAsync(true, onSuccess, onFailure);
compareAsync(false, onSuccess, onFailure);
console.log('Doing something else');
// Outputs:
// "Doing something else"
// "Value was expected"
// "Value was unexpected/exceptional"
Il convient de noter que les rappels multiples ne doivent pas nécessairement s’exclure mutuellement - les deux méthodes peuvent être appelées. De même, compare()
pourrait être écrit avec des callbacks optionnels (en utilisant un noop comme valeur par défaut - voir le pattern Objet nul ).
Rappels et `this`
Souvent, lorsque vous utilisez un rappel, vous souhaitez accéder à un contexte spécifique.
function SomeClass(msg, elem) {
this.msg = msg;
elem.addEventListener('click', function() {
console.log(this.msg); // <= will fail because "this" is undefined
});
}
var s = new SomeClass("hello", someElement);
Solutions
Utiliser
bind
bind
génère effectivement une nouvelle fonction qui définitthis
qui a été transmis àbind
puis appelle la fonction d'origine.function SomeClass(msg, elem) { this.msg = msg; elem.addEventListener('click', function() { console.log(this.msg); }.bind(this)); // <=- bind the function to `this` }
Utilisez les fonctions de flèche
Fonctions Arrow lient automatiquement le courant
this
contexte.function SomeClass(msg, elem) { this.msg = msg; elem.addEventListener('click',() => { // <=- arrow function binds `this` console.log(this.msg); }); }
Vous souhaitez souvent appeler une fonction membre en transmettant idéalement tous les arguments passés à l'événement dans la fonction.
Solutions:
Utiliser bind
function SomeClass(msg, elem) { this.msg = msg; elem.addEventListener('click', this.handleClick.bind(this)); } SomeClass.prototype.handleClick = function(event) { console.log(event.type, this.msg); };
Utilisez les fonctions de flèche et l'opérateur de repos
function SomeClass(msg, elem) { this.msg = msg; elem.addEventListener('click', (...a) => this.handleClick(...a)); } SomeClass.prototype.handleClick = function(event) { console.log(event.type, this.msg); };
Pour les écouteurs d'événement DOM en particulier, vous pouvez implémenter l' interface
EventListener
function SomeClass(msg, elem) { this.msg = msg; elem.addEventListener('click', this); } SomeClass.prototype.handleEvent = function(event) { var fn = this[event.type]; if (fn) { fn.apply(this, arguments); } }; SomeClass.prototype.click = function(event) { console.log(this.msg); };
Rappel à l'aide de la fonction Flèche
L'utilisation de la fonction flèche comme fonction de rappel peut réduire les lignes de code.
La syntaxe par défaut de la fonction flèche est
() => {}
Cela peut être utilisé comme callback
Par exemple si nous voulons imprimer tous les éléments d'un tableau [1,2,3,4,5]
sans fonction de flèche, le code ressemblera à ceci
[1,2,3,4,5].forEach(function(x){
console.log(x);
}
Avec la fonction flèche, il peut être réduit à
[1,2,3,4,5].forEach(x => console.log(x));
Ici, la fonction de rappel function(x){console.log(x)}
est réduite à x=>console.log(x)