JavaScript est un langage de programmation asynchrone (non bloquant) et monothread, ce qui signifie qu'un seul processus peut être exécuté à la fois.
Dans les langages de programmation, l’enfer des rappels fait généralement référence à une manière inefficace d’écrire du code avec des appels asynchrones. Elle est également connue sous le nom de Pyramide du Destin.
L'enfer des rappels en JavaScript est appelé une situation dans laquelle un nombre excessif de fonctions de rappel imbriquées sont exécutées. Cela réduit la lisibilité et la maintenance du code. La situation de l'enfer des rappels se produit généralement lors d'opérations de requête asynchrones, telles que l'exécution de plusieurs requêtes API ou la gestion d'événements avec des dépendances complexes.
exemple de format json
Pour mieux comprendre l'enfer des rappels en JavaScript, comprenez d'abord les rappels et les boucles d'événements en JavaScript.
Rappels en JavaScript
JavaScript considère tout comme un objet, comme les chaînes, les tableaux et les fonctions. Par conséquent, le concept de rappel nous permet de passer la fonction en argument à une autre fonction. La fonction de rappel terminera l'exécution en premier et la fonction parent sera exécutée plus tard.
Les fonctions de rappel sont exécutées de manière asynchrone et permettent au code de continuer à s'exécuter sans attendre la fin de la tâche asynchrone. Lorsque plusieurs tâches asynchrones sont combinées et que chaque tâche dépend de sa tâche précédente, la structure du code devient compliquée.
Comprenons l'utilisation et l'importance des rappels. Supposons qu'un exemple soit une fonction qui prend trois paramètres, une chaîne et deux nombres. Nous voulons une sortie basée sur le texte de la chaîne avec plusieurs conditions.
Considérez l'exemple ci-dessous :
function expectedResult(action, x, y){ if(action === 'add'){ return x+y }else if(action === 'subtract'){ return x-y } } console.log(expectedResult('add',20,10)) console.log(expectedResult('subtract',30,10))
Sortir:
30 20
Le code ci-dessus fonctionnera correctement, mais nous devons ajouter plus de tâches pour rendre le code évolutif. Le nombre d'instructions conditionnelles continuera également d'augmenter, ce qui entraînera une structure de code désordonnée qui devra être optimisée et lisible.
Nous pouvons donc mieux réécrire le code comme suit :
function add(x,y){ return x+y } function subtract(x,y){ return x-y } function expectedResult(callBack, x, y){ return callBack(x,y) } console.log(expectedResult(add, 20, 10)) console.log(expectedResult(subtract, 30, 10))
Sortir:
30 20
Pourtant, le résultat sera le même. Mais dans l'exemple ci-dessus, nous avons défini son corps de fonction distinct et transmis la fonction comme fonction de rappel à la fonction attendueResult. Par conséquent, si nous souhaitons étendre la fonctionnalité des résultats attendus afin de pouvoir créer un autre corps fonctionnel avec une opération différente et l'utiliser comme fonction de rappel, cela facilitera la compréhension et améliorera la lisibilité du code.
Il existe d'autres exemples différents de rappels disponibles dans les fonctionnalités JavaScript prises en charge. Quelques exemples courants sont les écouteurs d'événements et les fonctions de tableau telles que mapper, réduire, filtrer, etc.
Pour mieux le comprendre, nous devons comprendre le passage par valeur et le passage par référence de JavaScript.
JavaScript prend en charge deux types de types de données : primitifs et non primitifs. Les types de données primitifs sont indéfinis, nuls, chaîne et booléens, qui ne peuvent pas être modifiés, ou nous pouvons dire comparativement immuables ; Les types de données non primitifs sont des tableaux, des fonctions et des objets qui peuvent être modifiés ou mutables.
Passer par référence transmet l'adresse de référence d'une entité, comme une fonction peut être prise comme argument. Ainsi, si la valeur dans cette fonction est modifiée, cela modifiera la valeur d'origine, qui est disponible en dehors de la fonction.
En comparaison, le concept de passage par valeur ne modifie pas sa valeur d'origine, qui est disponible en dehors du corps de la fonction. Au lieu de cela, il copiera la valeur dans deux emplacements différents en utilisant leur mémoire. JavaScript a identifié tous les objets par leur référence.
En JavaScript, addEventListener écoute les événements tels que le clic, le survol et la sortie de la souris et prend le deuxième argument comme fonction qui sera exécutée une fois l'événement déclenché. Cette fonction est utilisée par concept de référence et transmise en utilisant sans parenthèses.
Considérez l'exemple ci-dessous ; dans cet exemple, nous avons passé une fonction greet comme argument dans addEventListener comme fonction de rappel. Il sera invoqué lorsque l'événement click est déclenché :
Test.html :
Javascript Callback Example <h3>Javascript Callback</h3> Click Here to Console const button = document.getElementById('btn'); const greet=()=>{ console.log('Hello, How are you?') } button.addEventListener('click', greet)
Sortir:
Dans l'exemple ci-dessus, nous avons passé une fonction greet comme argument dans addEventListener comme fonction de rappel. Il sera invoqué lorsque l'événement click sera déclenché.
De même, le filtre est également un exemple de fonction de rappel. Si nous utilisons un filtre pour parcourir un tableau, il prendra une autre fonction de rappel comme argument pour traiter les données du tableau. Considérez l'exemple ci-dessous ; dans cet exemple, nous utilisons la fonction supérieure pour imprimer le nombre supérieur à 5 dans le tableau. Nous utilisons la fonction isGreater comme fonction de rappel dans la méthode filter.
const arr = [3,10,6,7] const isGreater = num => num > 5 console.log(arr.filter(isGreater))
Sortir:
[ 10, 6, 7 ]
L'exemple ci-dessus montre que la fonction supérieure est utilisée comme fonction de rappel dans la méthode de filtrage.
Pour mieux comprendre les rappels et les boucles d'événements en JavaScript, parlons du JavaScript synchrone et asynchrone :
JavaScript synchrone
Voyons quelles sont les fonctionnalités d'un langage de programmation synchrone. La programmation synchrone présente les fonctionnalités suivantes :
Blocage de l'exécution : Le langage de programmation synchrone prend en charge la technique d'exécution bloquante, ce qui signifie qu'il bloque l'exécution des instructions suivantes. Les instructions existantes seront exécutées. Il réalise ainsi l'exécution prévisible et déterministe des instructions.
Flux séquentiel : La programmation synchrone prend en charge le flux d'exécution séquentiel, ce qui signifie que chaque instruction est exécutée de manière séquentielle, comme l'une après l'autre. Le programme de langage attend qu'une instruction soit terminée avant de passer à la suivante.
Simplicité: Souvent, la programmation synchrone est considérée comme facile à comprendre car on peut prédire son ordre du flux d'exécution. Généralement, c’est linéaire et facile à prédire. Les petites applications sont bonnes à développer sur ces langages car elles peuvent gérer l’ordre critique des opérations.
Gestion directe des erreurs : Dans un langage de programmation synchrone, la gestion des erreurs est très simple. Si une erreur se produit lors de l'exécution d'une instruction, elle générera une erreur et le programme pourra la détecter.
En un mot, la programmation synchrone a deux fonctionnalités principales : une seule tâche est exécutée à la fois et l'ensemble suivant de tâches suivantes ne sera abordé qu'une fois la tâche en cours terminée. Il s’ensuit une exécution de code séquentielle.
Ce comportement de la programmation lors de l'exécution d'une instruction, le langage crée une situation de code en bloc car chaque tâche doit attendre que la tâche précédente soit terminée.
Mais lorsque les gens parlent de JavaScript, la réponse est toujours déroutante, qu'elle soit synchrone ou asynchrone.
Dans les exemples évoqués ci-dessus, lorsque nous utilisions une fonction comme rappel dans la fonction de filtre, elle était exécutée de manière synchrone. C’est pourquoi on parle d’exécution synchrone. La fonction de filtrage doit attendre que la fonction supérieure termine son exécution.
Par conséquent, la fonction de rappel est également appelée rappels bloquants, car elle bloque l'exécution de la fonction parent dans laquelle elle a été invoquée.
Principalement, JavaScript est considéré comme synchrone et de nature bloquante à thread unique. Mais en utilisant quelques approches, nous pouvons le faire fonctionner de manière asynchrone en fonction de différents scénarios.
Maintenant, comprenons le JavaScript asynchrone.
JavaScript asynchrone
Le langage de programmation asynchrone se concentre sur l'amélioration des performances de l'application. Les rappels peuvent être utilisés dans de tels scénarios. Nous pouvons analyser le comportement asynchrone de JavaScript par l'exemple ci-dessous :
function greet(){ console.log('greet after 1 second') } setTimeout(greet, 1000)
D'après l'exemple ci-dessus, la fonction setTimeout prend un rappel et une durée en millisecondes comme arguments. Le rappel est invoqué après le temps mentionné (ici 1 s). En un mot, la fonction attendra 1 seconde pour son exécution. Maintenant, jetez un œil au code ci-dessous :
function greet(){ console.log('greet after 1 second') } setTimeout(greet, 1000) console.log('first') console.log('Second')
Sortir:
first Second greet after 1 second
À partir du code ci-dessus, les messages du journal après setTimeout seront exécutés en premier pendant que le minuteur passera. Par conséquent, il en résulte une seconde, puis le message d'accueil après un intervalle de temps d'une seconde.
En JavaScript, setTimeout est une fonction asynchrone. Chaque fois que nous appelons la fonction setTimeout, elle enregistre une fonction de rappel (greet dans ce cas) à exécuter après le délai spécifié. Cependant, cela ne bloque pas l’exécution du code suivant.
Dans l'exemple ci-dessus, les messages de journal sont les instructions synchrones qui s'exécutent immédiatement. Ils ne dépendent pas de la fonction setTimeout. Par conséquent, ils exécutent et enregistrent leurs messages respectifs sur la console sans attendre le délai spécifié dans setTimeout.
Pendant ce temps, la boucle d'événements en JavaScript gère les tâches asynchrones. Dans ce cas, il attend que le délai spécifié (1 seconde) s'écoule, et une fois ce délai écoulé, il récupère la fonction de rappel (salut) et l'exécute.
Ainsi, l'autre code après la fonction setTimeout s'exécutait lors de son exécution en arrière-plan. Ce comportement permet à JavaScript d'effectuer d'autres tâches en attendant la fin de l'opération asynchrone.
Nous devons comprendre la pile d'appels et la file d'attente de rappel pour gérer les événements asynchrones en JavaScript.
Considérez l'image ci-dessous :
changer le nom du répertoire Linux
D'après l'image ci-dessus, un moteur JavaScript typique se compose d'une mémoire tas et d'une pile d'appels. La pile d'appels exécute tout le code sans attendre lorsqu'elle est poussée vers la pile.
La mémoire tas est chargée d'allouer la mémoire aux objets et aux fonctions au moment de l'exécution chaque fois qu'ils sont nécessaires.
Désormais, nos moteurs de navigateur se composent de plusieurs API Web telles que DOM, setTimeout, console, fetch, etc., et le moteur peut accéder à ces API à l'aide de l'objet fenêtre global. À l'étape suivante, certaines boucles d'événements jouent le rôle de gardien qui sélectionne les demandes de fonction dans la file d'attente de rappel et les pousse dans la pile. Ces fonctions, comme setTimeout, nécessitent un certain temps d'attente.
Revenons maintenant à notre exemple, la fonction setTimeout ; lorsque la fonction est rencontrée, le minuteur est enregistré dans la file d'attente de rappel. Après cela, le reste du code est poussé dans la pile d'appels et est exécuté une fois que la fonction atteint sa limite de minuterie, il expire et la file d'attente de rappel pousse la fonction de rappel, qui a la logique spécifiée et est enregistrée dans la fonction de délai d'attente. . Ainsi, il sera exécuté après l’heure spécifiée.
Scénarios d’enfer de rappel
Maintenant, nous avons discuté des rappels, synchrones, asynchrones et d'autres sujets pertinents pour l'enfer des rappels. Comprenons ce qu'est l'enfer des rappels en JavaScript.
La situation dans laquelle plusieurs rappels sont imbriqués est connue sous le nom d'enfer des rappels, car la forme de son code ressemble à une pyramide, également appelée « pyramide du malheur ».
L'enfer des rappels rend plus difficile la compréhension et la maintenance du code. Nous pouvons surtout voir cette situation en travaillant dans le nœud JS. Par exemple, considérons l'exemple ci-dessous :
getArticlesData(20, (articles) => { console.log('article lists', articles); getUserData(article.username, (name) => { console.log(name); getAddress(name, (item) => { console.log(item); //This goes on and on... } })
Dans l'exemple ci-dessus, getUserData prend un nom d'utilisateur qui dépend de la liste d'articles ou doit être extrait de la réponse getArticles qui se trouve à l'intérieur de l'article. getAddress a également une dépendance similaire, qui dépend de la réponse de getUserData. Cette situation est appelée l’enfer du rappel.
pause java
Le fonctionnement interne de l'enfer des rappels peut être compris avec l'exemple ci-dessous :
Comprenons que nous devons effectuer la tâche A. Pour effectuer une tâche, nous avons besoin de certaines données de la tâche B. De même ; nous avons différentes tâches qui dépendent les unes des autres et s'exécutent de manière asynchrone. Ainsi, il crée une série de fonctions de rappel.
Comprenons les promesses en JavaScript et comment elles créent des opérations asynchrones, nous permettant d'éviter d'écrire des rappels imbriqués.
Promesses JavaScript
En JavaScript, les promesses ont été introduites dans ES6. C'est un objet avec un revêtement syntaxique. En raison de son comportement asynchrone, il s'agit d'un autre moyen d'éviter d'écrire les rappels pour les opérations asynchrones. De nos jours, les API Web comme fetch() sont implémentées en utilisant le prometteur, qui fournit un moyen efficace d'accéder aux données du serveur. Cela a également amélioré la lisibilité du code et constitue un moyen d’éviter d’écrire des rappels imbriqués.
Les promesses dans la vie réelle expriment la confiance entre deux ou plusieurs personnes et l’assurance qu’une chose particulière se produira sûrement. En JavaScript, une promesse est un objet qui garantit à la production une valeur unique dans le futur (si nécessaire). La promesse en JavaScript est utilisée pour gérer et gérer les opérations asynchrones.
La promesse renvoie un objet qui garantit et représente l'achèvement ou l'échec des opérations asynchrones et sa sortie. Il s'agit d'un proxy pour une valeur sans connaître le résultat exact. Il est utile pour les actions asynchrones de fournir une éventuelle valeur de réussite ou une raison d'échec. Ainsi, les méthodes asynchrones renvoient les valeurs comme une méthode synchrone.
Généralement, les promesses ont les trois états suivants :
- Accompli : l'état accompli correspond au moment où une action appliquée a été résolue ou terminée avec succès.
- En attente : l'état En attente correspond au moment où la demande est en cours de traitement et que l'action appliquée n'a été ni résolue ni rejetée et est toujours dans son état initial.
- Rejeté : l'état rejeté se produit lorsque l'action appliquée a été rejetée, provoquant l'échec de l'opération souhaitée. La cause du rejet peut être n’importe quoi, y compris la panne du serveur.
La syntaxe des promesses :
let newPromise = new Promise(function(resolve, reject) { // asynchronous call is made //Resolve or reject the data });
Vous trouverez ci-dessous un exemple de rédaction des promesses :
Ceci est un exemple de rédaction d’une promesse.
function getArticleData(id) { return new Promise((resolve, reject) => { setTimeout(() => { console.log('Fetching data....'); resolve({ id: id, name: 'derik' }); }, 5000); }); } getArticleData('10').then(res=> console.log(res))
Dans l’exemple ci-dessus, nous pouvons voir comment utiliser efficacement les promesses pour faire une requête depuis le serveur. On peut observer que la lisibilité du code est augmentée dans le code ci-dessus que dans les rappels. Les promesses fournissent des méthodes telles que .then() et .catch(), qui nous permettent de gérer l'état de l'opération en cas de succès ou d'échec. On peut préciser les cas pour les différents états des promesses.
Async/Attendre en JavaScript
C'est une autre façon d'éviter l'utilisation de rappels imbriqués. Async/Await nous permet d'utiliser les promesses beaucoup plus efficacement. Nous pouvons éviter d’utiliser le chaînage de méthodes .then() ou .catch(). Ces méthodes dépendent également des fonctions de rappel.
Async/Await peut être utilisé précisément avec Promise pour améliorer les performances de l'application. Il a résolu en interne les promesses et a fourni le résultat. De plus, encore une fois, il est plus lisible que les méthodes () ou catch().
Nous ne pouvons pas utiliser Async/Await avec les fonctions de rappel normales. Pour l'utiliser, il faut rendre une fonction asynchrone en écrivant un mot-clé async avant le mot-clé function. Cependant, en interne, il utilise également le chaînage.
Vous trouverez ci-dessous un exemple de Async/Await :
async function displayData() { try { const articleData = await getArticle(10); const placeData = await getPlaces(article.name); const cityData = await getCity(place) console.log(city); } catch (err) { console.log('Error: ', err.message); } } displayData();
Pour utiliser Async/Await, la fonction doit être spécifiée avec le mot-clé async et le mot-clé wait doit être écrit à l'intérieur de la fonction. L'async arrêtera son exécution jusqu'à ce que la promesse soit résolue ou rejetée. Elle reprendra lorsque la Promesse sera distribuée. Une fois résolue, la valeur de l'expression d'attente sera stockée dans la variable la contenant.
Résumé:
En un mot, nous pouvons éviter les rappels imbriqués en utilisant les promesses et async/await. En dehors de cela, nous pouvons suivre d’autres approches, telles que l’écriture de commentaires, et diviser le code en composants distincts peut également être utile. Mais, de nos jours, les développeurs préfèrent utiliser async/await.
Conclusion:
L'enfer des rappels en JavaScript est appelé une situation dans laquelle un nombre excessif de fonctions de rappel imbriquées sont exécutées. Cela réduit la lisibilité et la maintenance du code. La situation de l'enfer des rappels se produit généralement lors d'opérations de requête asynchrones, telles que l'exécution de plusieurs requêtes API ou la gestion d'événements avec des dépendances complexes.
Pour mieux comprendre l’enfer des rappels en JavaScript.
JavaScript considère tout comme un objet, comme les chaînes, les tableaux et les fonctions. Par conséquent, le concept de rappel nous permet de passer la fonction en argument à une autre fonction. La fonction de rappel terminera l'exécution en premier et la fonction parent sera exécutée plus tard.
Les fonctions de rappel sont exécutées de manière asynchrone et permettent au code de continuer à s'exécuter sans attendre la fin de la tâche asynchrone.