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


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


541
2018-05-05 10:18


origine


Réponses:


Les développeurs sont souvent confus par ce qu'est un rappel en raison du nom de la chose damnée.

Une fonction de rappel est une fonction qui est:

  • accessible par une autre fonction, et
  • est invoqué après la première fonction si cette première fonction se termine

Une bonne façon d'imaginer comment une fonction de rappel fonctionne est que c'est une fonction qui est "appelé à l'arrière"de la fonction dans laquelle il est passé.

Peut-être qu'un meilleur nom serait un "appeler après" fonction.

Cette construction est très utile pour le comportement asynchrone où nous voulons qu'une activité ait lieu à chaque fois qu'un événement précédent se termine.

Pseudocode:

// A function which accepts another function as an argument
// (and will automatically invoke that function when it completes - note that there is no explicit call to callbackFunction)
funct printANumber(int number, funct callbackFunction) {
    printout("The number you provided is: " + number);
}

// a function which we will use in a driver function as a callback function
funct printFinishMessage() {
    printout("I have finished printing numbers.");
}

// Driver method
funct event() {
   printANumber(6, printFinishMessage);
}

Résultat si vous avez appelé event ():

The number you provided is: 6
I have finished printing numbers.

L'ordre de la sortie ici est important. Puisque les fonctions de rappel sont appelées par la suite, "J'ai fini d'imprimer les numéros" est imprimé en dernier, pas en premier.

Les rappels sont appelés en raison de leur utilisation avec les langages de pointeur. Si vous n'utilisez pas l'un d'eux, ne travaillez pas sur le nom 'callback'. Comprenez simplement que c'est juste un nom pour décrire une méthode qui est fournie comme argument à une autre méthode, telle que quand la méthode parent est appelée (n'importe quelle condition, comme un clic de bouton, un tick etc.) et son corps de méthode se termine, la fonction de rappel est alors invoquée.

Certains langages prennent en charge des constructions où plusieurs arguments de fonction de rappel sont supportés et sont appelés en fonction de la fin de la fonction parent (un callback est appelé dans le cas où la fonction parent est terminée, un autre est appelé dans le cas où la fonction parent erreur spécifique, etc.).


527
2017-09-26 01:04



Définition opaque

Une fonction de rappel est une fonction que vous fournissez à un autre morceau de code, ce qui lui permet d'être appelé par ce code.

Exemple contredit

Pourquoi voudriez-vous faire cela? Disons qu'il y a un service que vous devez invoquer. Si le service revient immédiatement, vous venez de:

  1. Appeler
  2. Attendez le résultat
  3. Continuez une fois que le résultat arrive

Par exemple, supposons que le service était le factorial fonction. Quand vous voulez la valeur de 5!, vous invoqueriez factorial(5)et les étapes suivantes se produiraient:

  1. Votre emplacement d'exécution actuel est sauvegardé (sur la pile, mais ce n'est pas important)

  2. L'exécution est confiée à factorial

  3. Quand factorial complète, il met le résultat quelque part, vous pouvez y accéder

  4. L'exécution revient là où elle était en [1]

Supposons maintenant factorial a pris beaucoup de temps, parce que vous lui donnez des nombres énormes et qu'il doit fonctionner sur un cluster de supercalculateurs quelque part. Disons que vous attendez 5 minutes pour retourner votre résultat. Vous pourriez:

  1. Gardez votre design et exécutez votre programme la nuit quand vous dormez, de sorte que vous ne regardiez pas l'écran la moitié du temps

  2. Concevez votre programme pour faire d'autres choses tout en factorial fait sa chose

Si vous choisissez la deuxième option, les rappels peuvent fonctionner pour vous.

Conception de bout en bout

Afin d'exploiter un schéma de rappel, ce que vous voulez, c'est pouvoir appeler factorial de la manière suivante:

factorial(really_big_number, what_to_do_with_the_result)

Le deuxième paramètre, what_to_do_with_the_result, est une fonction que vous envoyez à factorial, dans l'espoir que factorial l'appellera sur son résultat avant de revenir.

Oui, cela signifie que factorial doit avoir été écrit pour supporter les rappels.

Supposons maintenant que vous voulez pouvoir passer un paramètre à votre callback. Maintenant tu ne peux pas, parce que tu ne vas pas l'appeler, factorial est. Alors factorial doit être écrit pour vous permettre de transmettre vos paramètres, et il les transmettra à votre callback quand il l'appellera. Cela pourrait ressembler à ceci:

factorial (number, callback, params)
{
    result = number!   // i can make up operators in my pseudocode
    callback (result, params)
}

Maintenant que factorial permet ce modèle, votre rappel pourrait ressembler à ceci:

logIt (number, logger)
{
    logger.log(number)
}

et votre appel à factorial serait

factorial(42, logIt, logger)

Que faire si vous voulez retourner quelque chose de logIt? Eh bien, vous ne pouvez pas, parce que factorial ne fait pas attention à cela.

Eh bien, pourquoi ne peut pas factorial retourne juste ce que ton rappel retourne?

Le rendant non bloquant

Depuis l'exécution est destinée à être remis au rappel quand factorial est terminé, il ne devrait vraiment rien retourner à son appelant. Et idéalement, il lancerait d'une manière ou d'une autre son travail dans un autre thread / processus / machine et reviendrait immédiatement pour que vous puissiez continuer, peut-être quelque chose comme ceci:

factorial(param_1, param_2, ...)
{
    new factorial_worker_task(param_1, param_2, ...);
    return;
}

C'est maintenant un "appel asynchrone", ce qui signifie que lorsque vous l'appelez, il revient immédiatement mais n'a pas encore vraiment fait son travail. Vous avez donc besoin de mécanismes pour le vérifier, et pour obtenir son résultat quand il est fini, et votre programme est devenu plus complexe dans le processus.

Et en passant, en utilisant ce modèle le factorial_worker_task peut lancer votre callback de manière asynchrone et revenir immédiatement.

Donc que fais-tu?

La réponse est de rester dans le modèle de rappel. Chaque fois que vous voulez écrire

a = f()
g(a)

et f doit être appelé de manière asynchrone, vous allez plutôt écrire

f(g)

g est passé en rappel.

Cela modifie fondamentalement la topologie de flux de votre programme, et prend un certain temps pour s'y habituer.

Votre langage de programmation pourrait vous aider beaucoup en vous donnant un moyen de créer des fonctions à la volée. Dans le code immédiatement ci-dessus, la fonction g pourrait être aussi petit que print (2*a+1). Si votre langue exige que vous définissiez ceci comme une fonction séparée, avec un nom et une signature totalement inutiles, alors votre vie deviendra désagréable si vous utilisez beaucoup ce modèle.

Si, d'un autre côté, votre langue vous permet de créer des lambdas, alors vous êtes en bien meilleur état. Vous finirez par écrire quelque chose comme

f( func(a) { print(2*a+1); })

ce qui est tellement plus agréable.

Comment passer le rappel

Comment passeriez-vous la fonction de rappel à factorial? Eh bien, vous pourriez le faire de plusieurs façons.

  1. Si la fonction appelée s'exécute dans le même processus, vous pouvez passer un pointeur de fonction

  2. Ou peut-être que vous voulez maintenir un dictionnaire de fn name --> fn ptr dans votre programme, auquel cas vous pourriez passer le nom

  3. Peut-être que votre langage vous permet de définir la fonction en place, possible en tant que lambda! En interne, il crée une sorte d'objet et transmet un pointeur, mais vous n'avez pas à vous en préoccuper.

  4. La fonction que vous appelez est peut-être en cours d'exécution sur une machine entièrement distincte et vous l'appelez en utilisant un protocole réseau tel que HTTP. Vous pouvez exposer votre rappel en tant que fonction appelable HTTP et transmettre son URL.

Vous avez eu l'idée.

La récente hausse des rappels

Dans cette ère du Web que nous avons saisie, les services que nous invoquons sont souvent sur le réseau. Nous n'avons souvent aucun contrôle sur ces services, c'est-à-dire que nous ne les avons pas écrits, nous ne les maintenons pas, nous ne pouvons pas nous assurer qu'ils sont en place ou qu'ils fonctionnent.

Mais nous ne pouvons pas nous attendre à ce que nos programmes bloquent pendant que nous attendons que ces services répondent. Conscients de cela, les fournisseurs de services conçoivent souvent des API en utilisant le modèle de rappel.

JavaScript supporte très bien les rappels, par ex. avec des lambdas et des fermetures. Et il y a beaucoup d'activité dans le monde JavaScript, aussi bien sur le navigateur que sur le serveur. Il existe même des plateformes JavaScript en cours de développement pour mobile.

À mesure que nous avançons, de plus en plus d'entre nous écriront du code asynchrone, pour lequel cette compréhension sera essentielle.


176
2018-05-04 06:34



Notez que le rappel est un mot.

La page de rappel wikipedia l'explique très bien.

citation de la page wikipedia:

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


87
2018-05-05 10:19



Une réponse profane serait que c'est une fonction qui n'est pas appelée par vous mais plutôt par l'utilisateur ou par le navigateur après qu'un certain événement s'est produit ou après que du code ait été traité.


40
2017-09-15 16:48



Une fonction de rappel est celle qui devrait être appelée lorsqu'une certaine condition est remplie. Au lieu d'être appelée immédiatement, la fonction de rappel est appelée à un certain moment dans le futur.

Généralement, il est utilisé lors du démarrage d'une tâche qui se terminera de manière asynchrone (c'est-à-dire qui se terminera un certain temps après le retour de la fonction d'appel).

Par exemple, une fonction pour demander une page Web peut demander à son appelant de fournir une fonction de rappel qui sera appelée lorsque la page Web aura fini de télécharger.


37
2018-05-05 16:07



Je crois que ce jargon de «rappel» a été utilisé à tort dans beaucoup d'endroits. Ma définition serait quelque chose comme:

Une fonction de rappel est une fonction que vous passez à quelqu'un et laissez   Ils l'appellent à un moment donné.

Je pense que les gens lisent juste la première phrase de la définition du wiki:

un rappel est une référence au code exécutable, ou un morceau de   code exécutable, qui est passé en argument à un autre code.

J'ai travaillé avec beaucoup d'API, voir divers exemples de mauvais. Beaucoup de gens ont tendance à nommer un pointeur de fonction (une référence au code exécutable) ou des fonctions anonymes (un morceau de code exécutable) "callback", s'ils ne sont que des fonctions, pourquoi avez-vous besoin d'un autre nom?

En fait, seule la deuxième phrase de la définition du wiki révèle les différences entre une fonction de rappel et une fonction normale:

Cela permet à une couche logicielle de niveau inférieur d'appeler une sous-routine (ou   fonction) définie dans une couche de niveau supérieur.

donc la différence est de savoir qui vous allez passer la fonction et comment votre fonction passée va être appelée. Si vous définissez simplement une fonction et la transmettez à une autre fonction et l'appelez directement dans ce corps de fonction, ne l'appelez pas callback. La définition dit que votre fonction passée sera appelée par la fonction "niveau inférieur".

J'espère que les gens peuvent arrêter d'utiliser ce mot dans un contexte ambigu, cela ne peut pas aider les gens à mieux comprendre que pire.


34
2017-07-20 10:58



Les rappels sont plus facilement décrits en termes de système téléphonique. Un appel de fonction est analogue à appeler quelqu'un au téléphone, lui poser une question, obtenir une réponse et raccrocher; l'ajout d'un rappel change l'analogie de sorte que, après lui avoir posé une question, vous lui donniez aussi votre nom et votre numéro pour qu'elle vous rappelle avec la réponse. 

- Paul Jakubik, "Implémentations de rappel en C ++"


26
2018-01-11 01:13



Gardons les choses simples. Qu'est-ce qu'une fonction de rappel?

Exemple par parabole et analogie

J'ai une secrétaire. Chaque jour, je lui demande: (i) de déposer le courrier sortant de l'entreprise au bureau de poste, et après elle l'a fait, à faire: (ii) quelle que soit la tâche que j'ai écrite pour elle sur l'un de ces notes autocollantes.

Maintenant, quelle est la tâche sur la note collante? La tâche varie d'un jour à l'autre.

Supposons que ce jour-là, je lui demande d'imprimer certains documents. Alors je l'écris sur la note autocollante, et je l'épingle sur son bureau avec le courrier sortant qu'elle doit poster.

En résumé:

  1. d'abord, elle doit déposer le courrier et
  2. juste après c'est fait, elle doit imprimer certains documents.

La fonction de rappel est cette deuxième tâche: l'impression de ces documents. Parce que c'est fait APRÈS que le courrier est déposé, et aussi parce que la note autocollante lui disant d'imprimer le document lui est donnée avec le courrier dont elle a le plus besoin.

Faisons maintenant le lien avec le vocabulaire de la programmation

  • Le nom de la méthode dans ce cas est: DropOffMail.
  • Et la fonction de rappel est: PrintOffDocuments. Le PrintOffDocuments est la fonction de rappel parce que nous voulons que le secrétaire le fasse, seulement après l'exécution de DropOffMail.
  • Donc je passerais: PrintOffDocuments comme un "argument" à la méthode DropOffMail, c'est un point important.

C'est tout. Rien de plus. J'espère que cela vous a éclairci - et sinon, postez un commentaire et je ferai de mon mieux pour clarifier.


21
2018-02-29 12:26