web-dev-qa-db-fra.com

Comment expliquer les rappels en anglais? En quoi diffèrent-ils d'appeler une fonction d'une autre?

Comment expliquer les rappels en anglais? En quoi diffèrent-ils d'appeler une fonction d'une autre fonction en prenant un contexte de la fonction appelante? Comment expliquer leur pouvoir à un programmeur novice?

331
Yahoo-Me

Souvent, une application doit exécuter différentes fonctions en fonction de son contexte/état. Pour cela, nous utilisons une variable dans laquelle nous stockerions les informations sur la fonction à appeler. En fonction de ses besoins, l'application définira cette variable avec les informations sur la fonction à appeler et appellera la fonction à l'aide de la même variable.

En javascript, l'exemple est ci-dessous. Ici, nous utilisons l’argument de la méthode comme variable où nous stockons des informations sur la fonction.

function processArray(arr, callback) {
    var resultArr = new Array(); 
    for (var i = arr.length-1; i >= 0; i--)
        resultArr[i] = callback(arr[i]);
    return resultArr;
}

var arr = [1, 2, 3, 4];
var arrReturned = processArray(arr, function(arg) {return arg * -1;});
// arrReturned would be [-1, -2, -3, -4]
113
Niraj Nawanit

Je vais essayer de garder cela simple. Un "callback" est une fonction appelée par une autre fonction qui prend la première fonction en paramètre. La plupart du temps, un "rappel" est une fonction appelée lorsque quelque chose se produit. Ce quelque chose peut être appelé un "événement" dans le langage programmé.

Imaginez ce scénario: vous attendez un colis dans quelques jours. Le paquet est un cadeau pour votre voisin. Par conséquent, une fois que vous recevez le paquet, vous voulez le faire parvenir aux voisins. Vous êtes en dehors de la ville et vous laissez donc des instructions à votre conjoint.

Vous pouvez leur dire de récupérer le colis et de l'apporter aux voisins. Si votre épouse était aussi stupide qu’un ordinateur, elle s’asseoirait à la porte et attendrait le colis jusqu’à ce qu’il vienne (il ne ferait rien d’autre), puis une fois qu’il serait livré, il le rapporterait aux voisins. Mais il y a un meilleur moyen. Dites à votre conjoint que, une fois qu'ils ont reçu le colis, ils doivent l'emporter chez leurs voisins. Ensuite, ils peuvent vivre normalement normalement JUSQU’À ce qu’ils reçoivent le colis.

Dans notre exemple, la réception du paquet est "l'événement" et son apport aux voisins est le "rappel". Votre conjoint "exécute" vos instructions pour amener le colis uniquement lorsque le colis arrive. Beaucoup mieux!

Ce genre de pensée est évident dans la vie quotidienne, mais les ordinateurs n’ont pas le même sens commun. Considérez comment les programmeurs écrivent normalement dans un fichier:

fileObject = open(file)
# now that we have WAITED for the file to open, we can write to it
fileObject.write("We are writing to the file.")
# now we can continue doing the other, totally unrelated things our program does

Ici, nous attendons que le fichier s'ouvre avant de l'écrire. Ceci "bloque" le flux d'exécution, et notre programme ne peut rien faire d'autre que ce dont il pourrait avoir besoin! Et si on pouvait faire ça à la place:

# we pass writeToFile (A CALLBACK FUNCTION!) to the open function
fileObject = open(file, writeToFile)
# execution continues flowing -- we don't wait for the file to be opened
# ONCE the file is opened we write to it, but while we wait WE CAN DO OTHER THINGS!

Il s'avère que nous faisons cela avec certaines langues et certains frameworks. C'est vraiment cool! Consultez Node.js pour vous familiariser avec ce type de réflexion.

511
Josh Imhoff

Comment expliquer les rappels en anglais?

En clair, une fonction de rappel est semblable à un travailleur qui "rappelle" son gestionnaire quand il a terminé une tâche.

En quoi diffèrent-ils d'appeler une fonction d'une autre fonction en prenant un contexte de la fonction appelante?

Il est vrai que vous appelez une fonction depuis une autre fonction, mais le rappel est traité comme un objet. Vous pouvez donc modifier la fonction à appeler en fonction de l'état du système (comme le modèle de conception de stratégie).

Comment expliquer leur pouvoir à un programmeur novice?

La puissance des rappels est facilement perceptible sur les sites Web de type AJAX qui doivent extraire des données d’un serveur. Le téléchargement des nouvelles données peut prendre un certain temps. Sans les rappels, toute votre interface utilisateur se "figerait" pendant le téléchargement des nouvelles données, ou vous auriez besoin d'actualiser la page entière plutôt qu'une partie de celle-ci. Avec un rappel, vous pouvez insérer une image "Chargement en cours" et la remplacer par les nouvelles données une fois chargées.

Un code sans rappel:

function grabAndFreeze() {
    showNowLoading(true);
    var jsondata = getData('http://yourserver.com/data/messages.json');
    /* User Interface 'freezes' while getting data */
    processData(jsondata);
    showNowLoading(false);
    do_other_stuff(); // not called until data fully downloaded
}

function processData(jsondata) { // do something with the data
   var count = jsondata.results ? jsondata.results.length : 0;
   $('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
   $('#results_messages').html(jsondata.results || '(no new messages)');
}

Avec rappel:

Voici un exemple avec un rappel, utilisant la méthode getJSON de jQuery:

function processDataCB(jsondata) { // callback: update UI with results
   showNowLoading(false);
   var count = jsondata.results ? jsondata.results.length : 0;
   $('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
   $('#results_messages').html(jsondata.results || '(no new messages)');
}

function grabAndGo() { // and don't freeze
    showNowLoading(true);
    $('#results_messages').html(now_loading_image);
    $.getJSON("http://yourserver.com/data/messages.json", processDataCB);
    /* Call processDataCB when data is downloaded, no frozen User Interface! */
    do_other_stuff(); // called immediately
}

Avec fermeture:

Le rappel a souvent besoin d'accéder à state à partir de la fonction appelante à l'aide d'un closure, qui est semblable à Worker qui doit obtenir des informations à partir de Manager avant il peut compléter sa tâche. Pour créer la closure, vous pouvez insérer la fonction en ligne afin qu'elle voie les données dans le contexte de l'appel:

/* Grab messages, chat users, etc by changing dtable. Run callback cb when done.*/
function grab(dtable, cb) { 
    if (null == dtable) { dtable = "messages"; }
    var uiElem = "_" + dtable;
    showNowLoading(true, dtable);
    $('#results' + uiElem).html(now_loading_image);
    $.getJSON("http://yourserver.com/user/"+dtable+".json", cb || function (jsondata) {
       // Using a closure: can "see" dtable argument and uiElem variables above.
       var count = jsondata.results ? jsondata.results.length : 0, 
           counterMsg = ['Fetched', count, 'new', dtable].join(' '),
           // no new chatters/messages/etc
           defaultResultsMsg = ['(no new ', dtable, ')'].join(''); 
       showNowLoading(false, dtable);
       $('#counter' + uiElem).text(counterMsg);
       $('#results'+ uiElem).html(jsondata.results || defaultResultsMsg);
    });
    /* User Interface calls cb when data is downloaded */

    do_other_stuff(); // called immediately
}

Usage:

// update results_chatters when chatters.json data is downloaded:
grab("chatters"); 
// update results_messages when messages.json data is downloaded
grab("messages"); 
// call myCallback(jsondata) when "history.json" data is loaded:
grab("history", myCallback); 

Fermeture

Enfin, voici une définition de closure de Douglas Crockford :

Les fonctions peuvent être définies à l'intérieur d'autres fonctions. La fonction interne a accès aux vars et aux paramètres de la fonction externe. Si une référence à une fonction interne survit (par exemple, en tant que fonction de rappel), les vars de la fonction externe survivent également.

Voir également:

82
user508994

Je suis abasourdi de voir tant de personnes intelligentes ne pas souligner le fait que le mot "rappel" a été utilisé de deux manières incohérentes.

Les deux méthodes impliquent la personnalisation d'une fonction en transmettant des fonctionnalités supplémentaires (une définition de fonction, anonyme ou nommée) à une fonction existante. c'est à dire.

customizableFunc(customFunctionality)

Si la fonctionnalité personnalisée est simplement connectée au bloc de code, vous avez personnalisé la fonction, comme cela.

    customizableFucn(customFunctionality) {
      var data = doSomthing();
      customFunctionality(data);
      ...
    }

Bien que ce type de fonctionnalité injectée soit souvent appelé un "rappel", il n’ya rien de contingent à ce sujet. Un exemple très évident est la méthode forEach dans laquelle une fonction personnalisée est fournie en tant qu'argument à appliquer à chaque élément d'un tableau pour modifier le tableau.

Mais ceci est fondamentalement distinct de l'utilisation de fonctions de "rappel" pour programmation asynchrone, comme dans AJAX ou node.js, ou simplement dans l'attribution de fonctionnalités à des événements d'interaction de l'utilisateur (comme des clics de souris). ) Dans ce cas, l'idée est d'attendre qu'un événement éventuel se produise avant d'exécuter la fonctionnalité personnalisée. Ceci est évident dans le cas d'une interaction utilisateur, mais est également important dans les processus d'E/S (entrée/sortie) qui peuvent prendre du temps, comme la lecture de fichiers à partir d'un disque. C’est là que le terme "rappel" prend tout son sens. Une fois qu'un processus d'E/S est démarré (par exemple, demander à un fichier d'être lu sur un disque ou à un serveur pour renvoyer des données à partir d'une requête http), un asynchrone programme n'attend pas pour finir. Il peut exécuter les tâches planifiées suivantes et ne répondre à la fonctionnalité personnalisée qu'après avoir été informé que le fichier lu ou la demande http est terminé (ou que la requête a échoué) et que les données sont disponibles pour la fonctionnalité personnalisée. C'est comme appeler une entreprise au téléphone et laisser votre numéro de "rappel", afin qu'ils puissent vous appeler quand quelqu'un est disponible pour vous contacter. C'est mieux que de rester en ligne pour qui sait combien de temps et ne pas pouvoir s'occuper d'autres affaires.

L'utilisation asynchrone implique par nature un moyen d'écouter l'événement souhaité (par exemple, l'achèvement du processus d'entrée/sortie), de sorte que, lorsqu'il se produit (et uniquement lorsqu'il se produit), la fonctionnalité de "rappel" personnalisée est exécutée. Dans l'exemple évident AJAX, lorsque les données arrivent réellement du serveur, la fonction "callback" est déclenchée pour utiliser ces données pour modifier le DOM et donc redessiner la fenêtre du navigateur dans cette mesure.

Récapituler. Certaines personnes utilisent le mot "rappel" pour désigner n'importe quel type de fonctionnalité personnalisée pouvant être injectée dans une fonction existante en tant qu'argument. Mais, du moins pour moi, l’utilisation la plus appropriée du mot est celle où la fonction "callback" injectée est utilisée de manière asynchrone - à exécuter uniquement lors de l’apparition d’un événement pour lequel elle attend d’être notifiée.

37
Robert Polevoi

En termes non programmables, un rappel est un complément à compléter dans un programme.

Un élément commun à de nombreux formulaires papier est "Personne à appeler en cas d'urgence". Il y a une ligne blanche là-bas. Vous écrivez dans le nom et le numéro de téléphone de quelqu'un. En cas d'urgence, cette personne est appelée.

  • Tout le monde reçoit le même formulaire vierge, mais
  • Tout le monde peut écrire un numéro de téléphone d'urgence différent.

C'est la clé. Vous ne modifiez pas le formulaire (le code, généralement celui de quelqu'un d'autre). Cependant, vous pouvez renseigner des informations manquantes (votre numéro).

Exemple 1:

Les rappels sont utilisés comme méthodes personnalisées, éventuellement pour ajouter/modifier le comportement d'un programme. Par exemple, prenons du code C qui remplit une fonction, mais ne sait pas comment imprimer le résultat. Tout ce qu'il peut faire, c'est créer une chaîne. Quand il essaie de comprendre quoi faire avec la chaîne, il voit une ligne vide. Mais, le programmeur vous a donné le blanc pour écrire votre rappel!

Dans cet exemple, vous n'utilisez pas de crayon pour remplir un blanc sur une feuille de papier, vous utilisez la fonction set_print_callback(the_callback).

  • La variable vide dans le module/code est la ligne vide,
  • set_print_callback est le crayon,
  • et the_callback sont les informations que vous complétez.

Vous avez maintenant rempli cette ligne vide dans le programme. Chaque fois que la sortie doit être imprimée, elle examine cette ligne vierge et suit les instructions (c.-à-d., Appelez la fonction que vous avez indiquée). En pratique, cela permet d'imprimer à l'écran, dans un fichier journal, sur une imprimante. via une connexion réseau ou une combinaison des deux. Vous avez rempli le vide avec ce que vous voulez faire.

Exemple 2:

Quand on vous dit que vous devez appeler un numéro d'urgence, vous allez lire ce qui est écrit sur le formulaire papier, puis vous appelez le numéro que vous avez lu. Si cette ligne est vide, rien ne sera fait.

La programmation graphique fonctionne de la même manière. Quand un bouton est cliqué, le programme doit déterminer ce qu'il faut faire ensuite. Il va chercher le rappel. Ce rappel se trouve dans un espace vierge intitulé "Voici ce que vous faites lorsque vous cliquez sur Button1"

La plupart des IDE rempliront automatiquement le vide pour vous (écrivez la méthode de base) lorsque vous le demandez (par exemple, button1_clicked). Cependant, ce blanc peut avoir toute méthode que vous voulez bien. Vous pouvez appeler la méthode run_computations ou butter_the_biscuits tant que vous mettez le nom de ce rappel dans le blanc approprié. Vous pouvez mettre "555-555-1212" dans le numéro d’urgence en blanc. Cela n'a pas beaucoup de sens, mais c'est permis.


Note finale: Cette ligne vide que vous complétez avec le rappel? Il peut être effacé et réécrit à volonté. (si vous devriez ou non, c'est une autre question, mais cela fait partie de leur pouvoir)

26
JoeyG

Johny, le programmeur a besoin d’une agrafeuse. Il se rend alors au service des fournitures de bureau et en demande une. Après avoir rempli le formulaire de demande, il peut soit rester là et attendre que le commis se rende dans l’entrepôt pour chercher l’agrafeuse (comme un appel de fonction de blocage). ) ou aller faire autre chose entre-temps.

comme cela prend généralement du temps, johny met une note avec le formulaire de demande leur demandant de l'appeler lorsque l'agrafeuse est prête à être prise en main. Il peut donc faire autre chose, comme faire la sieste sur son bureau.

21
tovmeod

Toujours mieux de commencer avec un exemple :).

Supposons que vous avez deux modules A et B.

Vous voulez que le module A soit notifié quand un événement/une condition survient dans le module B. Cependant, le module B n'a aucune idée de votre module A. Il ne sait que l'adresse d'une fonction particulière (du module A ) via un pointeur de fonction fourni par le module A.

Donc, tout ce que B doit faire maintenant, c’est le "rappel" dans le module A quand un événement/une condition particulière se produit en utilisant le pointeur de fonction. Un peut effectuer un traitement ultérieur dans la fonction de rappel.

*) Un avantage évident est que vous extrayez tout du module B du module A. Le module B ne doit pas se soucier de qui/quel module est.

20
Gargi Srinivas

Imaginez que vous ayez besoin d’une fonction qui retourne 10 au carré, vous écrivez donc une fonction:

function tenSquared() {return 10*10;}

Plus tard, vous aurez besoin de 9 au carré pour écrire une autre fonction:

function nineSquared() {return 9*9;}

Vous finirez par les remplacer par une fonction générique:

function square(x) {return x*x;}

La même pensée s'applique aux rappels. Vous avez une fonction qui fait quelque chose et quand c'est fait, appelle doA:

function computeA(){
    ...
    doA(result);
}

Plus tard, vous voulez que la même fonction appelle doB au lieu de cela, vous pouvez dupliquer toute la fonction:

function computeB(){
    ...
    doB(result);
}

Ou vous pouvez passer une fonction de rappel en tant que variable et n’avoir que la fonction une fois:

function compute(callback){
    ...
    callback(result);
}

Ensuite, vous devez simplement appeler compute (doA) et compute (doB).

Au-delà de la simplification du code, il permet au code asynchrone de vous informer qu'il a terminé en appelant votre fonction arbitraire à la fin, comme lorsque vous appelez quelqu'un au téléphone et laissez un numéro de rappel.

18
Brian Nickel

Vous vous sentez malade alors allez chez le médecin. Il vous examine et détermine que vous avez besoin de médicaments. Il prescrit des médicaments et appelle l'ordonnance dans votre pharmacie locale. Tu vas à la maison. Plus tard, votre pharmacie appelle pour vous dire que votre ordonnance est prête. Vous allez le chercher.

11
effigy

Il y a deux points à expliquer: le fonctionnement d'un rappel (transmission d'une fonction pouvant être appelée sans connaître le contexte), l'autre à quoi elle sert (gestion des événements de manière asynchrone).

L'analogie d'attendre l'arrivée d'un colis qui a été utilisée par d'autres réponses est une bonne explication pour les deux. Dans un programme informatique, vous diriez à l'ordinateur d'attendre un colis. Normalement, il resterait là et attendrait (et ne ferait rien d'autre) jusqu'à ce que le colis arrive, éventuellement indéfiniment s'il n'arrive jamais. Pour les humains, cela semble idiot, mais sans autres mesures, cela est tout à fait naturel pour un ordinateur.

Maintenant, le rappel serait la sonnerie à votre porte. Vous fournissez au service des colis un moyen de vous informer de l’arrivée du colis sans qu’ils aient à savoir où (même si) vous vous trouvez dans la maison, ni comment fonctionne la cloche. (Par exemple, certaines "cloches" envoient en réalité un appel téléphonique.) Étant donné que vous avez fourni une "fonction de rappel" qui peut être "appelée" à tout moment, hors contexte, vous pouvez maintenant arrêter de vous asseoir devant le porche et "gérer les événement "(de l'arrivée du colis) quand c'est l'heure.

10
Hanno Fietz

Les rappels sont plus facilement décrits en termes de système téléphonique. Un appel de fonction revient à appeler une personne au téléphone, lui poser une question, obtenir une réponse et raccrocher; ajouter un rappel modifie l’analogie de sorte en lui posant une question, vous lui donnez également votre nom et votre numéro afin qu'elle puisse vous rappeler avec la réponse. - Paul Jakubik, "Callback Implementations in C++"

6
DejanLekic

Imaginez qu'un ami quitte votre maison et que vous lui dites: "Appelez-moi quand vous rentrerez à la maison pour que je sache que vous êtes bien arrivé"; c'est (littéralement) un rappel. C'est ce qu'est une fonction de rappel, quelle que soit la langue. Vous souhaitez qu'une procédure vous redonne le contrôle une fois la tâche terminée, vous lui attribuez donc une fonction à utiliser pour vous rappeler.

En Python, par exemple,

grabDBValue( (lambda x: passValueToGUIWindow(x) ))

grabDBValue pourrait être écrit pour ne saisir qu'une valeur d'une base de données, puis vous permettre de spécifier ce qu'il faut réellement faire avec la valeur, afin qu'il accepte une fonction. Vous ne savez pas quand ou si grabDBValue reviendra, mais si/quand il le fera, vous saurez ce que vous voulez qu'il fasse. Ici, je passe dans une fonction anonyme (ou lambda) qui envoie la valeur à une fenêtre graphique. Je pourrais facilement changer le comportement du programme en faisant ceci:

grabDBValue( (lambda x: passToLogger(x) ))

Les callbacks fonctionnent bien dans les langages où les fonctions sont première classe, tout comme les entiers habituels, les chaînes de caractères, les booléens, etc. En C, vous pouvez "passer" une fonction en passant un pointeur sur elle. et l'appelant peut utiliser cela; en Java, l'appelant demandera une classe statique d'un certain type avec un certain nom de méthode car il n'y a pas de fonctions ("méthodes", en réalité) en dehors des classes; et dans la plupart des autres langages dynamiques, vous pouvez simplement passer une fonction avec une syntaxe simple.

Protip:

Dans les langues avec portée lexicale (comme Scheme ou Perl), vous pouvez tirer un tour comme celui-ci:

my $var = 2;
my $val = someCallerBackFunction(sub callback { return $var * 3; });
# Perlistas note: I know the sub doesn't need a name, this is for illustration

$val dans ce cas sera 6 parce que le rappel a accès aux variables déclarées dans l'environnement lexical où il a été défini. La portée lexicale et les rappels anonymes constituent une combinaison puissante qui mérite une étude plus approfondie pour le programmeur débutant.

6
gatlin

Imaginons que vous deviez me confier une tâche potentiellement longue: obtenez le nom des cinq premières personnes que vous rencontrerez. Cela peut prendre des jours si je suis dans une région peu peuplée. Vous n'êtes pas vraiment intéressé à rester assis sur vos mains pendant que je cours, alors vous dites: "Quand vous aurez la liste, appelez-moi sur mon portable et relisez-la-moi. Voici le numéro.".

Vous m'avez donné une référence de rappel - une fonction que je suis censé exécuter pour transférer un traitement supplémentaire.

En JavaScript, cela pourrait ressembler à ceci:

var lottoNumbers = [];
var callback = function(theNames) {
  for (var i=0; i<theNames.length; i++) {
    lottoNumbers.Push(theNames[i].length);
  }
};

db.executeQuery("SELECT name " +
                "FROM tblEveryOneInTheWholeWorld " +
                "ORDER BY proximity DESC " +
                "LIMIT 5", callback);

while (lottoNumbers.length < 5) {
  playGolf();
}
playLotto(lottoNumbers);

Cela pourrait probablement être amélioré de nombreuses façons. Par exemple, vous pouvez effectuer un deuxième rappel: si cela prend plus d'une heure, appelez le téléphone rouge et dites à la personne qui a répondu que votre délai a expiré.

6
steamer25

Sans rappel ni les autres ressources de programmation spéciales (comme le filetage, etc.), n programme est exactement une séquence d'instructions exécutées séquentiellement les unes après les autres, et même avec une sorte de "comportement dynamique" déterminé sous certaines conditions, tous les scénarios possibles doivent être préalablement programmés.

Donc, si nous devons fournir un comportement dynamique réel à un programme, nous pouvons utiliser le rappel. Avec callback, vous pouvez indiquer, par paramètres, à un programme d’appeler un autre programme fournissant certains paramètres définis précédemment et pouvant attendre certains résultats (, il s’agit du contrat ou de la signature de l’opération ), afin que ces résultats puissent être produits/traités par un programme tiers inconnu auparavant.

Cette technique est la base du polymorphisme appliqué aux programmes, fonctions, objets et à toutes les autres unités de code exécutées par des ordinateurs.

Le monde humain utilisé comme exemple de rappel est bien expliqué lorsque vous faites un travail, supposons que vous êtes un peintre (). Vous êtes ici le programme principal, qui peint ) et appelez parfois votre client pour lui demander d’approuver le résultat de votre travail. Il décide donc si la photo est bonne ( votre client est le programme tiers ).

Dans l'exemple ci-dessus, vous êtes un peintre et "déléguez" à d'autres le travail d'approbation du résultat, l'image est le paramètre et chaque nouveau client (la "fonction" rappelée) modifie le résultat de votre travail et détermine ce qu'il veut. à propos de l'image ( la décision prise par les clients est le résultat renvoyé par la "fonction de rappel" ).

J'espère que cette explication peut être utile.

6
Luciano

Vous avez du code que vous voulez exécuter. Normalement, lorsque vous l'appelez, vous attendez que l'opération soit terminée avant de poursuivre (ce qui peut rendre votre application grise/produire un temps de rotation pour un curseur).

Une autre méthode consiste à exécuter ce code en parallèle et à poursuivre votre propre travail. Mais que se passe-t-il si votre code d'origine doit faire différentes choses en fonction de la réponse du code qu'il a appelé? Eh bien, dans ce cas, vous pouvez indiquer le nom/l’emplacement du code que vous souhaitez appeler une fois terminé. Ceci est un "rappel".

Code normal: Demander des informations-> Process Information-> Traiter les résultats du traitement-> Continuer à faire autre chose.

Avec les rappels: Demander des informations-> Traiter les informations-> Continuer à faire autre chose. Et à un moment ultérieur-> Traiter avec les résultats du traitement.

6
Andrew Ducker

Un rappel est une fonction qui sera appelée par une deuxième fonction. Cette deuxième fonction ne sait pas à l'avance quelle fonction elle appellera. Ainsi, le identité de la fonction de rappel est stocké quelque part ou transmis à la deuxième fonction en tant que paramètre. Selon le langage de programmation, cette "identité" peut être l'adresse du rappel, ou un autre type de pointeur, ou bien le nom de la fonction. Le principe est le même, nous stockons ou transmettons des informations qui identifient sans ambiguïté la fonction.

Le moment venu, la deuxième fonction peut appeler le rappel, en fournissant des paramètres en fonction des circonstances. Il peut même choisir le rappel parmi un ensemble de rappels possibles. Le langage de programmation doit fournir une sorte de syntaxe permettant à la deuxième fonction d’appeler le rappel, connaissant son "identité".

Ce mécanisme a de nombreuses utilisations possibles. Avec les rappels, le concepteur d’une fonction peut le personnaliser en l’appelant quels que soient les rappels fournis. Par exemple, une fonction de tri peut prendre un rappel en tant que paramètre et ce rappel peut être une fonction permettant de comparer deux éléments afin de décider lequel vient en premier.

En passant, en fonction du langage de programmation, le mot "fonction" dans la discussion ci-dessus pourrait être remplacé par "bloc", "fermeture", "lambda", etc.

5
David Casseres

Habituellement, nous envoyions des variables aux fonctions. Supposons que vous ayez une tâche pour laquelle la variable doit être traitée avant d'être argumentée - vous pouvez utiliser le rappel.

function1(var1, var2) est la méthode habituelle.

Et si je veux que var2 soit traité puis envoyé comme argument? function1(var1, function2(var2))

Il s'agit d'un type de rappel - où function2 exécute du code et renvoie une variable à la fonction initiale.

5
Nishant

Pour enseigner les rappels, vous devez d'abord apprendre le pointeur. Une fois que les élèves ont compris l’idée d’un pointeur sur une variable, l’idée des rappels sera plus facile. En supposant que vous utilisiez C/C++, vous pouvez suivre ces étapes.

  • Commencez par montrer à vos étudiants comment utiliser et manipuler des variables en utilisant des pointeurs à côté des identificateurs de variable normaux.
  • Apprenez-leur ensuite qu'il y a des choses qui ne peuvent être faites qu'avec des pointeurs (comme passer une variable par référence).
  • Puis, expliquez-leur comment le code ou les fonctions exécutables ressemblent à d’autres données (ou variables) en mémoire. Ainsi, les fonctions ont aussi des adresses ou des pointeurs.
  • Puis montrez-leur comment les fonctions peuvent être appelées avec des pointeurs de fonction et dites que ce sont des rappels.
  • Maintenant, la question est, pourquoi tous ces tracas pour appeler des fonctions? Quel est l'avantage? Comme les pointeurs de données, le pointeur de fonction, également appelé callbacks, présente certains avantages par rapport aux identificateurs normaux.
  • Le premier est que les identificateurs de fonction ou les noms de fonction ne peuvent pas être utilisés comme données normales. Je veux dire, vous ne pouvez pas créer une structure de données avec des fonctions (comme un tableau ou une liste de fonctions liée). Mais avec les rappels, vous pouvez créer un tableau, une liste chaînée ou les utiliser avec d’autres données, comme dans un dictionnaire de paires clé-valeur, d’arbres ou autre. C'est un avantage puissant. Et d'autres avantages sont en réalité enfant de celui-ci.
  • L'utilisation la plus courante des rappels est visible dans la programmation du pilote d'événement. Où une ou plusieurs fonctions sont exécutées sur la base d’un signal entrant. Avec les rappels, un dictionnaire peut être maintenu pour mapper les signaux avec des rappels. La résolution du signal d'entrée et l'exécution du code correspondant deviennent alors beaucoup plus faciles.
  • La deuxième utilisation des rappels qui me viennent à l’esprit est celle des fonctions d’ordre supérieur. La fonction qui prend d'autres fonctions comme arguments d'entrée. Et pour envoyer des fonctions sous forme d'arguments, nous avons besoin de rappels. Un exemple peut être une fonction qui prend un tableau et un rappel. Ensuite, il effectue le rappel sur chacun des éléments du tableau et renvoie les résultats dans un autre tableau. Si nous transmettons à la fonction un rappel doublé, nous obtenons un tableau à valeurs doublées. Si nous passons un rappel au carré, nous obtenons des carrés. Pour les racines carrées, envoyez simplement le rappel approprié. Cela ne peut pas être fait avec des fonctions normales.

Il pourrait y avoir beaucoup plus de choses. Impliquez les étudiants et ils vont découvrir. J'espère que cela t'aides.

4
Gulshan

Une explication métaphorique:

J'ai un colis que je veux livrer à un ami et je veux aussi savoir quand mon ami le recevra.

Alors je prends le colis à la poste et leur demande de le livrer. Si je veux savoir quand mon ami recevra le colis, j'ai deux options:

(a) Je peux attendre au bureau de poste jusqu'à ce qu'il soit livré.

(b) Je recevrai un courrier électronique à la livraison.

L'option (b) est analogue à un rappel.

4
tonylo

En clair, un rappel est une promesse. Joe, Jane, David et Samantha partagent un covoiturage pour se rendre au travail. Joe conduit aujourd'hui. Jane, David et Samantha ont plusieurs options:

  1. Vérifiez la fenêtre toutes les 5 minutes pour voir si Joe est absent
  2. Continuez à faire leur truc jusqu'à ce que Joe sonne à la porte.

Option 1: Cela ressemble plus à un exemple d'interrogation où Jane serait bloquée dans une "boucle" vérifiant si Joe est à l'extérieur. Jane ne peut rien faire d'autre dans l'intervalle.

Option 2: Ceci est l'exemple de rappel. Jane dit à Joe de sonner à sa porte quand il sera dehors. Elle lui donne une "fonction" pour sonner à la porte. Joe n'a pas besoin de savoir comment fonctionne la sonnette ni où il se trouve, il a simplement besoin d'appeler cette fonction, c'est-à-dire de sonner à la porte quand il est là.

Les rappels sont motivés par des "événements". Dans cet exemple, "l'événement" est l'arrivée de Joe. En Ajax, par exemple, les événements peuvent être "succès" ou "échec" de la demande asynchrone et chacun peut avoir le même rappel ou des rappels différents.

En termes d'applications JavaScript et de rappels. Nous devons également comprendre les "fermetures" et le contexte de l'application. Ce à quoi "ceci" fait référence peut facilement confondre les développeurs JavaScript. Dans cet exemple, dans le cadre de la méthode/callback "ring_the_door_bell ()" de chaque personne, il peut exister d'autres méthodes que chaque personne doit suivre en fonction de leur routine matinale ex. "éteins la télévision()". Nous voudrions que "ceci" fasse référence à l'objet "Jane" ou à l'objet "David" afin que chacun puisse configurer ce qu'il lui reste à faire avant que Joe ne les récupère. C’est là que la mise en place du rappel avec Joe nécessite de parodier la méthode afin que "ceci" se réfère au bon objet.

J'espère que ça t'as aidé!

3
Nael El Shawwa

Je pense que c'est une tâche plutôt facile à expliquer.

Au premier rappel, ce ne sont que des fonctions ordinaires.
Et plus loin encore, nous appelons cette fonction (appelons-la A) de l'intérieur d'une autre fonction (appelons-la B).

La magie à ce sujet est que je décide, qui fonction devrait être appelée par la fonction de extérieur B.

Au moment où j'écris la fonction B, je ne sais pas quelle fonction de rappel devrait être appelée. Au moment où j'appelle la fonction B, je dis aussi à cette fonction d'appeler la fonction A. C'est tout.

3
yunzen

Qu'est-ce qu'une fonction de rappel?

La réponse simple à cette première question est qu'une fonction de rappel est une fonction appelée via un pointeur de fonction. Si vous passez le pointeur (adresse) d'une fonction en tant qu'argument à un autre, lorsque ce pointeur est utilisé pour appeler la fonction à laquelle il pointe, il est dit qu'un rappel est effectué.

La fonction de rappel est difficile à suivre, mais elle est parfois très utile. Surtout quand vous concevez des bibliothèques. La fonction de rappel revient à demander à votre utilisateur de vous donner un nom de fonction, et vous appelez cette fonction dans certaines conditions.

Par exemple, vous écrivez une minuterie de rappel. Il vous permet de spécifier la durée et la fonction à appeler, et la fonction sera rappelée en conséquence. “Exécuter ma fonction () toutes les 10 secondes pendant 5 fois”

Vous pouvez également créer un répertoire de fonctions en passant une liste de noms de fonctions et demander à la bibliothèque de rappeler en conséquence. "Rappel de réussite () en cas de succès, le rappel échoue () en cas d'échec."

Regardons un exemple simple de pointeur de fonction

void cbfunc()
{
     printf("called");
}

 int main ()
 {
                   /* function pointer */ 
      void (*callback)(void); 
                   /* point to your callback function */ 
      callback=(void *)cbfunc; 
                   /* perform callback */
      callback();
      return 0; 
}

Comment passer un argument à une fonction de rappel?

Observé ce pointeur de fonction pour implémenter le rappel prend void *, ce qui indique qu'il peut accepter n'importe quel type de variable, y compris la structure. Par conséquent, vous pouvez passer plusieurs arguments par structure.

typedef struct myst
{
     int a;
     char b[10];
}myst;

void cbfunc(myst *mt) 
{
     fprintf(stdout,"called %d %s.",mt->a,mt->b); 
}

int main() 
{
       /* func pointer */
    void (*callback)(void *);       //param
     myst m;
     m.a=10;
     strcpy(m.b,"123");       
     callback = (void*)cbfunc;    /* point to callback function */
     callback(&m);                /* perform callback and pass in the param */
     return 0;   
}
3
Sachin Mhetre

Un rappel est une enveloppe timbrée avec adresse. Lorsque vous appelez une fonction, cela revient à envoyer une lettre. Si vous souhaitez que cette fonction appelle une autre fonction, fournissez ces informations sous la forme d'une référence ou d'une adresse.

3
pete

"En programmation informatique, un rappel est une référence à un code exécutable, ou un morceau de code exécutable, qui est passé comme argument à un autre code. Cela permet à une couche logicielle de niveau inférieur d'appeler un sous-programme (ou une fonction) défini dans une couche de niveau supérieur. ”- Wikipedia

rappel en C utilisant le pointeur de fonction

En C, le rappel est implémenté à l'aide de la fonction Pointeur. Pointeur de fonction - comme son nom l'indique, est un pointeur sur une fonction.

Par exemple, int (* ptrFunc) ();

Ici, ptrFunc est un pointeur sur une fonction qui ne prend aucun argument et renvoie un entier. N'oubliez pas de mettre entre parenthèses, sinon le compilateur supposera que ptrFunc est un nom de fonction normal, qui ne prend rien et renvoie un pointeur sur un entier.

Voici du code pour illustrer le pointeur de la fonction.

#include<stdio.h>
int func(int, int);
int main(void)
{
    int result1,result2;
    /* declaring a pointer to a function which takes
       two int arguments and returns an integer as result */
    int (*ptrFunc)(int,int);

    /* assigning ptrFunc to func's address */                    
    ptrFunc=func;

    /* calling func() through explicit dereference */
    result1 = (*ptrFunc)(10,20);

    /* calling func() through implicit dereference */        
    result2 = ptrFunc(10,20);            
    printf("result1 = %d result2 = %d\n",result1,result2);
    return 0;
}

int func(int x, int y)
{
    return x+y;
}

Essayons maintenant de comprendre le concept de rappel en C en utilisant un pointeur de fonction.

Le programme complet contient trois fichiers: callback.c, reg_callback.h et reg_callback.c.

/* callback.c */
#include<stdio.h>
#include"reg_callback.h"

/* callback function definition goes here */
void my_callback(void)
{
    printf("inside my_callback\n");
}

int main(void)
{
    /* initialize function pointer to
    my_callback */
    callback ptr_my_callback=my_callback;                        
    printf("This is a program demonstrating function callback\n");
    /* register our callback function */
    register_callback(ptr_my_callback);                          
    printf("back inside main program\n");
    return 0;
}

/* reg_callback.h */
typedef void (*callback)(void);
void register_callback(callback ptr_reg_callback);


/* reg_callback.c */
#include<stdio.h>
#include"reg_callback.h"

/* registration goes here */
void register_callback(callback ptr_reg_callback)
{
    printf("inside register_callback\n");
    /* calling our callback function my_callback */
    (*ptr_reg_callback)();                               
}

Si nous courons ce programme, la sortie sera

Ceci est un programme démontrant le rappel de fonction dans register_callback dans my_callback dans le programme principal

La fonction de couche supérieure appelle une fonction de couche inférieure en tant qu'appel normal et le mécanisme de rappel permet à la fonction de couche inférieure d'appeler la fonction de couche supérieure via un pointeur sur une fonction de rappel.

rappel dans Java à l'aide de l'interface

Java n'a pas le concept de pointeur de fonction. Il implémente le mécanisme de rappel via son mécanisme d'interface. Ici, au lieu d'un pointeur de fonction, nous déclarons une interface ayant une méthode qui sera appelée lorsque l'appelé aura terminé sa tâche.

Laissez-moi le démontrer à travers un exemple:

l'interface de rappel

public interface Callback
{
    public void notify(Result result);
}

l'appelant ou la classe de niveau supérieur

public Class Caller implements Callback
{
Callee ce = new Callee(this); //pass self to the callee

//Other functionality
//Call the Asynctask
ce.doAsynctask();

public void notify(Result result){
//Got the result after the callee has finished the task
//Can do whatever i want with the result
}
}

fonction de calque ou de couche inférieure

public Class Callee {
Callback cb;
Callee(Callback cb){
this.cb = cb;
}

doAsynctask(){
//do the long running task
//get the result
cb.notify(result);//after the task is completed, notify the caller
}
}

rappel à l'aide d'un modèle EventListener

  • Élément de liste

Ce modèle est utilisé pour notifier de 0 à n nombres d'observateurs/auditeurs qu'une tâche particulière est terminée

  • Élément de liste

La différence entre le mécanisme de rappel et le mécanisme EventListener/Observer réside dans le fait que l'appelé avertit l'appelant unique, tandis que dans Eventlisener/Observer, l'appelant peut avertir toute personne intéressée par cet événement (la notification peut être transmise à d'autres parties du message). application qui n'a pas déclenché la tâche)

Laissez-moi l'expliquer à travers un exemple.

l'interface de l'événement

public interface Events {

public void clickEvent();
public void longClickEvent();
}

Widget de classe

package com.som_itsolutions.training.Java.exampleeventlistener;

import Java.util.ArrayList;
import Java.util.Iterator;

public class Widget implements Events{

    ArrayList<OnClickEventListener> mClickEventListener = new ArrayList<OnClickEventListener>(); 
    ArrayList<OnLongClickEventListener> mLongClickEventListener = new ArrayList<OnLongClickEventListener>();

    @Override
    public void clickEvent() {
        // TODO Auto-generated method stub
        Iterator<OnClickEventListener> it = mClickEventListener.iterator();
                while(it.hasNext()){
                    OnClickEventListener li = it.next();
                    li.onClick(this);
                }   
    }
    @Override
    public void longClickEvent() {
        // TODO Auto-generated method stub
        Iterator<OnLongClickEventListener> it = mLongClickEventListener.iterator();
        while(it.hasNext()){
            OnLongClickEventListener li = it.next();
            li.onLongClick(this);
        }

    }

    public interface OnClickEventListener
    {
        public void onClick (Widget source);
    }

    public interface OnLongClickEventListener
    {
        public void onLongClick (Widget source);
    }

    public void setOnClickEventListner(OnClickEventListener li){
        mClickEventListener.add(li);
    }
    public void setOnLongClickEventListner(OnLongClickEventListener li){
        mLongClickEventListener.add(li);
    }
}

Bouton de classe

public class Button extends Widget{
private String mButtonText;
public Button (){
} 
public String getButtonText() {
return mButtonText;
}
public void setButtonText(String buttonText) {
this.mButtonText = buttonText;
}
}

Case à cocher de la classe

public class CheckBox extends Widget{
private boolean checked;
public CheckBox() {
checked = false;
}
public boolean isChecked(){
return (checked == true);
}
public void setCheck(boolean checked){
this.checked = checked;
}
}

Classe d'activité

package com.som_itsolutions.training.Java.exampleeventlistener;

public class Activity implements Widget.OnClickEventListener
{
    public Button mButton;
    public CheckBox mCheckBox;
    private static Activity mActivityHandler;
    public static Activity getActivityHandle(){
        return mActivityHandler;
    }
    public Activity ()
    {
        mActivityHandler = this;
        mButton = new Button();
        mButton.setOnClickEventListner(this);
        mCheckBox = new CheckBox();
        mCheckBox.setOnClickEventListner(this);
        } 
    public void onClick (Widget source)
    {
        if(source == mButton){
            mButton.setButtonText("Thank you for clicking me...");
            System.out.println(((Button) mButton).getButtonText());
        }
        if(source == mCheckBox){
            if(mCheckBox.isChecked()==false){
                mCheckBox.setCheck(true);
                System.out.println("The checkbox is checked...");
            }
            else{
                mCheckBox.setCheck(false);
                System.out.println("The checkbox is not checked...");
            }       
        }
    }
    public void doSomeWork(Widget source){
        source.clickEvent();
    }   
}

Autre classe

public class OtherClass implements Widget.OnClickEventListener{
Button mButton;
public OtherClass(){
mButton = Activity.getActivityHandle().mButton;
mButton.setOnClickEventListner(this);//interested in the click event                        //of the button
}
@Override
public void onClick(Widget source) {
if(source == mButton){
System.out.println("Other Class has also received the event notification...");
}
}

Classe principale

public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Activity a = new Activity();
OtherClass o = new OtherClass();
a.doSomeWork(a.mButton);
a.doSomeWork(a.mCheckBox);
}
}

Comme vous pouvez le constater à partir du code ci-dessus, nous avons une interface appelée événements qui répertorie tous les événements pouvant survenir pour notre application. La classe Widget est la classe de base pour tous les composants d'interface utilisateur tels que Button, Checkbox. Ces composants d'interface utilisateur sont les objets qui reçoivent réellement les événements du code d'infrastructure. La classe Widget implémente l'interface Events et possède également deux interfaces imbriquées, à savoir OnClickEventListener et OnLongClickEventListener.

Ces deux interfaces sont chargées d'écouter les événements susceptibles de se produire sur les composants d'interface utilisateur dérivés du widget, tels que Button ou Checkbox. Par conséquent, si nous comparons cet exemple avec le précédent exemple Callback utilisant Java Interface, ces deux interfaces fonctionnent comme une interface Callback. Le code de niveau supérieur (Here Activity) implémente donc ces deux interfaces. Et chaque fois qu'un événement survient sur un widget, le code de niveau supérieur (ou la méthode de ces interfaces implémentée dans le code de niveau supérieur, ici Activité), sera appelé.

Permettez-moi maintenant de discuter de la différence fondamentale entre le modèle Callback et Eventlistener. Comme nous l'avons déjà mentionné, Callé ne peut être signalé qu'à un seul appelant. Mais dans le cas du modèle EventListener, toute autre partie ou classe de l'application peut s'inscrire pour les événements susceptibles de se produire sur le bouton ou la case à cocher. L'exemple de ce type de classe est OtherClass. Si vous voyez le code de OtherClass, vous constaterez qu'il s'est enregistré en tant qu'écouteur du ClickEvent susceptible de se produire dans le bouton défini dans l'activité. La partie intéressante est que, en plus de l'activité (l'appelant), cette OtherClass sera également notifiée chaque fois que l'événement click se produira sur le bouton.

2

Clair et simple: un rappel est une fonction que vous donnez à une autre fonction, de sorte qu'il puisse appeler.

Habituellement, il est appelé quand une opération est terminée. Comme vous créez le rappel avant de le donner à l'autre fonction, vous pouvez l'initialiser avec les informations de contexte du site de l'appel. C’est pourquoi elle est appelée appel * retour * - la première fonction rappelle dans le contexte à partir duquel elle a été appelée.

2
Andrei Vajna II

Un rappel est une méthode dont l'exécution est planifiée lorsqu'une condition est remplie.

Un exemple du "monde réel" est un magasin de jeux vidéo local. Vous attendez Half-Life 3. Au lieu de vous rendre au magasin tous les jours pour voir s’il est présent, vous enregistrez votre email sur une liste afin d’être averti de la disponibilité du jeu. Le courrier électronique devient votre "rappel" et la condition à remplir est la disponibilité du jeu.

Un exemple de "programmeur" est une page Web sur laquelle vous souhaitez exécuter une action lorsque vous cliquez sur un bouton. Vous enregistrez une méthode de rappel pour un bouton et continuez à effectuer d'autres tâches. Lorsque/si l'utilisateur sélectionne le bouton, le navigateur examinera la liste des rappels pour cet événement et appellera votre méthode.

Un rappel est un moyen de gérer les événements de manière asynchrone. Vous ne pouvez jamais savoir quand le rappel sera exécuté ou s'il sera exécuté du tout. L'avantage est que cela libère votre programme et les cycles de la CPU pour effectuer d'autres tâches en attendant la réponse.

2
Optimist

[édité] quand on a deux fonctions dites functionA et functionB , si functionA dépend de functionB .

alors nous appelons functionB comme fonction de rappel . Ceci est largement utilisé dans le framework Spring.

callback function wikipedia example

1

Pensez à une méthode comme donnant une tâche à un collègue. Une tâche simple pourrait être la suivante:

Solve these equations:
x + 2 = y
2 * x = 3 * y

Votre collègue fait les calculs avec diligence et vous donne le résultat suivant:

x = -6
y = -4

Mais votre collègue a un problème, il ne comprend pas toujours les notations, telles que ^, mais il les comprend par leur description. Tels que exponent. Chaque fois qu'il en trouve un, vous récupérez les éléments suivants:

I don't understand "^"

Cela vous oblige à réécrire tout votre jeu d'instructions après avoir expliqué ce que le personnage signifie à votre collègue, et il ne se souvient pas toujours entre les questions. Et il a également du mal à se souvenir de vos conseils, par exemple, il suffit de me demander. Il suit toujours vos instructions écrites du mieux qu’il peut.

Vous pensez à une solution, vous ajoutez simplement ce qui suit à toutes vos instructions:

If you have any questions about symbols, call me at extension 1234 and I will tell you its name.

Maintenant, chaque fois qu'il a un problème, il vous appelle et lui demande, plutôt que de vous donner une mauvaise réponse et de faire redémarrer le processus.

1
Guvante

Les rappels vous permettent d'insérer votre propre code dans un autre bloc de code à exécuter à un autre moment, ce qui modifie ou ajoute le comportement de cet autre bloc de code à vos besoins. Vous gagnez en flexibilité et en personnalisabilité tout en pouvant avoir un code plus facilement maintenable.

Moins de hardcode = plus facile à gérer et à changer = moins de temps = plus de valeur métier = génialité.

Par exemple, en javascript, en utilisant Underscore.js, vous pouvez trouver tous les éléments même dans un tableau comme celui-ci:

var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> [2, 4, 6]

Exemple avec la permission de Underscore.js: http://documentcloud.github.com/underscore/#filter

1
letuboy

fonctions de rappel:

Nous définissons un callback function nommé callback lui donnons un paramètre otherFunction et l'invoquons à l'intérieur du corps de la fonction.

function callback(otherFunction){
    otherFunction();
}

Lorsque nous appelons la fonction callback, elle attend un argument de type function. Nous l'invoquons donc avec une fonction anonyme. Cependant, une erreur est générée si l'argument n'est pas de type function.

callback(function(){console.log('SUCCESS!')}); 
callback(1); // error

Cuire l'exemple de la pizza. four pour cuire la base de pizza garnie d'ingrédients Maintenant, voici oven est le callback function. et pizza base with ingredients est la otherFunction.

Point à noter est que différents ingrédients de pizza produisent différents types de pizzas mais le four qui cuit il reste le même. C’est un peu un travail de callback function, qui attend toujours des fonctions avec des fonctionnalités différentes afin de produire différents résultats personnalisés.

0
Sagar Munjal

Ceci en termes de téléchargement d'une page Web:

Votre programme fonctionne sur un téléphone portable et demande la page Web http://www.google.com . Si vous écrivez votre programme de manière synchrone, la fonction que vous écrivez pour télécharger les données sera exécutée en continu jusqu'à ce que toutes les données soient téléchargées. Cela signifie que votre interface utilisateur ne s'actualisera pas et apparaîtra essentiellement figée. Si vous écrivez votre programme à l'aide de callbacks, vous demandez les données et dites "exécutez cette fonction lorsque vous avez terminé". Cela permet à l'interface utilisateur d'autoriser les interactions de l'utilisateur pendant le téléchargement du fichier. Une fois le téléchargement de la page Web terminé, votre fonction de résultat (rappel) est appelée et vous pouvez gérer les données.

En gros, cela vous permet de demander quelque chose et de continuer à exécuter en attendant le résultat. Une fois que le résultat vous est renvoyé via une fonction de rappel, vous pouvez reprendre l’opération où elle s’est arrêtée.

0
sokket