
En programmation , une fonction de rappel (callback) est un modèle de programmation où une référence de fonction est transmise d'un contexte (consommateur) à un autre (fournisseur) afin que ce dernier puisse appeler la fonction. Si la fonction accède à l'état ou aux fonctionnalités du consommateur, l'appel est alors effectué vers le consommateur – à l'inverse du flux d'exécution normal où un consommateur appelle un fournisseur.
Une fonction acceptant un paramètre de rappel peut être conçue pour rappeler la fonction avant de retourner à son appelant. Cependant, le plus souvent, une référence au rappel est stockée par le fournisseur afin de pouvoir appeler la fonction ultérieurement (appel différé ). Si le fournisseur appelle le rappel sur le même thread que le consommateur, l'appel est bloquant , c'est-à -dire synchrone . En revanche, si le fournisseur appelle le rappel sur un thread différent, l'appel est non bloquant , c'est-à -dire asynchrone .
On peut comparer une fonction de rappel à des instructions données à un tailleur concernant la livraison d'un costume prêt, comme appeler un numéro précis ou le livrer à une adresse donnée. Ces instructions constituent une fonction de rappel : une fonction fournie à l'avance pour être exécutée ultérieurement, souvent par une autre partie du système et pas nécessairement par celle qui l'a reçue.
La différence entre une référence de fonction générale et une fonction de rappel peut être subtile, et certains utilisent ces termes indifféremment. Toutefois, la distinction dépend généralement de l'intention du programmeur. Si l'intention est similaire à celle d' un rappel téléphonique – c'est-à-dire que le destinataire initial communique avec l' appelant initial – alors il s'agit d'une fonction de rappel.
Utiliser
Une fonction de rappel bloquante s'exécute dans le contexte d'exécution de la fonction qui la transmet. Une fonction de rappel différée peut s'exécuter dans un contexte différent, par exemple lors d' une interruption ou depuis un thread . De ce fait, une fonction de rappel différée peut être utilisée pour la synchronisation et la délégation de tâches à un autre thread.
Gestion des événements
Une fonction de rappel peut servir à la gestion d'événements. Souvent, le code consommateur enregistre une fonction de rappel pour un type d'événement particulier. Lorsque cet événement se produit, la fonction de rappel est appelée. Les fonctions de rappel sont fréquemment utilisées pour programmer l' interface utilisateur graphique (IUG) d'un programme s'exécutant dans un système de fenêtrage . L'application fournit une référence à une fonction de rappel personnalisée que le système de fenêtrage appelle. Ce dernier appelle cette fonction pour notifier l'application d'événements tels que les clics de souris et les frappes au clavier .
Action asynchrone
Une fonction de rappel peut être utilisée pour implémenter un traitement asynchrone.
L'appelant demande une action et fournit une fonction de rappel qui sera appelée une fois l'action terminée, ce qui peut se produire longtemps après la demande.
Polymorphisme
Une fonction de rappel peut être utilisée pour implémenter le polymorphisme . Dans le pseudocode suivant, say_hielle peut prendre soit `true` write_status, soit `false` write_error.
from typing import Callabledef write_status ( message : str ) -> None : write ( stdout , message )def write_error ( message : str ) -> None : write ( stderr , message )def say_hi ( write : Callable [[ str ], None ]) -> None : write ( "Hello world" )
Mise en œuvre
La technologie de rappel est implémentée différemment selon le langage de programmation .
En assembleur , C , C++ , Pascal , Modula-2 et autres langages, une fonction de rappel est stockée en interne sous forme de pointeur de fonction . L'utilisation d'un stockage commun permet à différents langages de partager directement des fonctions de rappel sans couche d'interopérabilité à la conception ou à l'exécution . Par exemple, l' API Windows est accessible via plusieurs langages, compilateurs et assembleurs. C++ permet également à des objets d'implémenter l'appel de fonction. La bibliothèque STL (Standard Template Library) accepte ces objets (appelés foncteurs ) comme paramètres. De nombreux langages dynamiques , tels que JavaScript , Lua , Python , Perl et PHP , permettent de passer un objet fonction. Les langages en ligne de commande (CLI) comme C# et Visual Basic (.NET) (VB.NET) fournissent une référence de fonction encapsulée et typée, appelée délégué . Les événements et les gestionnaires d'événements , utilisés dans les langages .NET , permettent la gestion des fonctions de rappel. Les langages de programmation fonctionnelle prennent généralement en charge les fonctions de première classe , qui peuvent être passées en tant que rappels à d'autres fonctions, stockées comme données ou renvoyées par des fonctions.
De nombreux langages, dont Perl, Python, Ruby , Smalltalk , C++ (11+), C# et VB.NET (versions récentes), ainsi que la plupart des langages fonctionnels, prennent en charge les expressions lambda , des fonctions anonymes à syntaxe en ligne qui servent généralement de fonctions de rappel. Dans certains langages, comme Scheme , ML , JavaScript, Perl, Python, Smalltalk, PHP (depuis la version 5.3.0), C++ (11+), Java (depuis la version 8), et bien d'autres, une lambda peut être une fermeture , c'est-à-dire accéder à des variables définies localement dans le contexte où elle est définie. Dans un langage de programmation orienté objet tel que Java (versions antérieures aux arguments de type fonction), le comportement d'une fonction de rappel peut être obtenu en passant un objet implémentant une interface. Les méthodes de cet objet sont alors des fonctions de rappel. En PL/I et ALGOL 60 , une procédure de rappel peut avoir besoin d'accéder aux variables locales des blocs conteneurs ; elle est donc appelée via une variable d'entrée contenant à la fois le point d'entrée et les informations de contexte.
Exemple de code
C
Les fonctions de rappel ont de nombreuses utilisations, par exemple dans la signalisation d'erreurs : un programme Unix peut ne pas vouloir se terminer immédiatement lorsqu'il reçoit SIGTERM , donc pour s'assurer que sa terminaison est gérée correctement, il enregistrerait la fonction de nettoyage comme une fonction de rappel.
#include <signal.h> #include <stdlib.h>#include <fcntl.h> #include <sys/types>struct Data { // ... };struct statique Data data = { // initialiser le contenu ici }; static volatile sigatomic_t flag = 0 ;void cleanupHandler ( int signum ) { flag = 1 ; int saveFile = open ( "savefile.dat" , O_WRONLY | O_CREATO , S_IWUSR ); write ( saveFile , data , sizeof data ); close ( saveFile ); }int main ( void ) { signal ( cleanupHandler , SIGTERM ); doStuff (); }
Une autre utilisation courante des rappels en C concerne les fonctions de la bibliothèque standard C utilisées pour le tri ( qsort()) et la recherche ( lsearch(), bsearch()) où une fonction de comparaison est passée comme argument à la routine pour déterminer l'ordre de classement.
#include <stdlib.h>struct Person { char name [ 20 ]; int age ; };Personnes [ 1000 ] ;// Compare les personnes par âge int compareAges ( struct Person * p1 , struct Person * p2 ) { return p2 -> age - p1 -> age ; }int main ( void ) { // ... int nombreDePersonnes = /* un nombre ici */ ;qsort ( nombreDePersonnes , sizeof ( struct Personne ), personnes , compareÂges ); // ... }
Les fonctions de rappel peuvent également servir à contrôler l'exécution d'une fonction : Xlib permet de spécifier des prédicats personnalisés pour déterminer si un programme souhaite gérer un événement. Dans le code CprintNumber() suivant, la fonction utilise un paramètre getNumbercomme fonction de rappel bloquante. printNumber()Elle est appelée avec getAnswerToMostImportantQuestion()ce paramètre, qui agit comme une fonction de rappel. À l'exécution, le résultat est : « Valeur : 42 ».
#include <stdio.h> #include <stdlib.h>void printNumber ( int ( * getNumber )( void )) { int val = getNumber (); printf ( "Valeur : %d " , val ); }int getAnswerToMostImportantQuestion ( void ) { return 42 ; }int main ( void ) { printNumber ( getAnswerToMostImportantQuestion ); return 0 ; }
C++
In C++, functors can be used in addition to function pointer. A functor is an object with operator() defined. For example, the objects in std::views are functors. This is an example of using functors in C++:
importstd;classMyCallback{public:voidoperator()(intx){std::println("Callback called with value: {}",x);}};template<typenameCallback>voidperformOperation(inta,Callbackcallback){std::println("Performing operation on: {}",a);callback(a);}intmain(){MyCallbackcallback;intvalue=10;performOperation(value,callback);return0;}
std::function<R(Args...)> is a type-erased wrapper for any callable objects, introduced in C++11:
importstd;usingstd::function;voidperformOperation(inta,function<void(int)>callback){std::println("Performing operation on: {}",a);callback(a);}intmain(){intvalue=10;performOperation(value,[](intx)->void{std::println("Callback called with value: {}",x);});return0;}
C#
In the following C# code, method Helper.PerformAction uses parameter callback as a blocking callback. Helper.PerformAction is called with Log which acts as a callback function. When run, the following is written to the console: "Callback was: Hello world".
namespaceWikipedia.Examples;utiliser le système ;class Helper { public void PerformAction ( Action < string > callback ) { callback ( "Hello world" ); } }public class Main { static void Log ( string str ) { Console . WriteLine ( $"Callback was: {str}" ); }static void Main ( string [ ] args ) { Helper helper = new (); helper.PerformAction ( Log ) ; } }
JavaScript
Dans le code JavaScript suivant , la fonction calculateutilise un paramètre operatecomme fonction de rappel bloquante. calculateElle est appelée avec multiply`is` puis avec sum`is`, qui servent de fonctions de rappel.
fonction calculer ( a , b , opérer ) { retourner opérer ( a , b ); } fonction multiplier ( a , b ) { retourner a * b ; } fonction somme ( a , b ) { retourner a + b ; } // affiche 20 alerte ( calculer ( 10 , 2 , multiplier )); // affiche 12 alerte ( calculer ( 10 , 2 , somme ));
La méthode `collection` .each()de la bibliothèque jQuery utilise la fonction qui lui est passée comme fonction de rappel bloquante. Elle appelle cette fonction pour chaque élément de la collection. Par exemple :
$ ( "li" ). each ( function ( index ) { console.log ( index + ": " + $ ( this ) .text ( )); } );
Les rappels différés sont couramment utilisés pour gérer les événements provenant de l'utilisateur, du client et des minuteurs. On peut en trouver des exemples addEventListenerdans Ajax et XMLHttpRequest .
En plus de l'utilisation de rappels dans le code source JavaScript, les fonctions C qui prennent une fonction sont prises en charge via js-ctypes.
Julia
Dans le code Juliacalculate suivant, la fonction accepte un paramètre operateutilisé comme fonction de rappel bloquante. calculateElle est appelée avec squarece paramètre, qui fait office de fonction de rappel.
julia> square ( val ) = val ^ 2 square(fonction générique avec 1 méthode) julia> calculate ( operate , val ) = operator ( val ) calculate(fonction générique avec 1 méthode) julia> calculate ( square , 5 ) 25
Kotlin
Dans le code KotlinaskAndAnswer suivant, la fonction utilise un paramètre getAnswercomme fonction de rappel bloquante. askAndAnswerElle est appelée avec getAnswerToMostImportantQuestionce paramètre, qui sert de fonction de rappel. L'exécution de ce code indiquera à l'utilisateur que la réponse à sa question est « 42 ».
fun main () { print ( "Entrez la question la plus importante : " ) val question = readLine () askAndAnswer ( question , :: getAnswerToMostImportantQuestion ) }fun getAnswerToMostImportantQuestion (): Int { return 42 }fun askAndAnswer ( question : String?, getAnswer : ( ) -> Int ) { println ( "Question : $ question " ) println ( "Réponse : ${ getAnswer () } " ) }
Lua
Dans ce code Luacalculate , la fonction accepte le operationparamètre qui sert de rappel bloquant. calculateElle est appelée avec les deux valeurs addet multiply, puis utilise une fonction anonyme pour effectuer la division.
fonction calculer ( a , b , opération ) retourner opération ( a , b ) finfonction multiplier ( a , b ) renvoie a * b finfonction ajouter ( a , b ) renvoie a + b finprint ( calculate ( 10 , 20 , multiply )) -- affiche 200 print ( calculate ( 10 , 20 , add )) -- affiche 30 -- un exemple de rappel utilisant une fonction anonyme print ( calculate ( 10 , 20 , function ( a , b ) return a / b) -- affiche 0.5 end ))
Python
Dans le code Pythoncalculate suivant, la fonction accepte un paramètre operateutilisé comme fonction de rappel bloquante. calculateElle est appelée avec squarece paramètre, qui sert de fonction de rappel.
def carré ( val : int ) -> int : return val ** 2def calculer ( opérer : Appelable [[ int ], int ], val : int ) -> int : retourner opérer ( val )# impressions : 25 imprimer ( calculer ( carré , 5 ))
Rebol et Rouge
Le code Rebol et Red suivant illustre l'utilisation des fonctions de rappel.
- Comme l'alerte requiert une chaîne de caractères, le formulaire génère une chaîne à partir du résultat du calcul.
- Les valeurs get-word! (c'est-à-dire :calc-product et :calc-sum) incitent l'interpréteur à renvoyer le code de la fonction plutôt que de l'évaluer avec la fonction.
- Le type de données ! références dans un bloc ! [float ! integer !] limitent le type des valeurs passées comme arguments.
Rouge [ Titre : « Exemple de rappel » ]calculer : func [ num1 [ nombre ! ] num2 [ nombre ! ] fonction-de-rappel [ fonction ! ] ][ fonction-de-rappel num1 num2 ]calc-product: func [ num1 [ nombre! ] num2 [ nombre! ] ][ num1 * num2 ]calc-sum: func [ num1 [ nombre! ] num2 [ nombre! ] ][ num1 + num2 ]; alertes 75, le produit de 5 et 15 formulaire d'alerte calculer 5 15 :calc-product; alertes 20, la somme de 5 et 15 formulaire d'alerte calculer 5 15 :calc-sum
Rouiller
Rust possède les traits Fn, FnMutet . FnOnce
fn call_with_one < F > ( func : F ) -> usize où F : Fn ( usize ) -> usize { func ( 1 ) }let double = | x | x * 2 ; assert_eq! ( call_with_one ( double ), 2 );