window.pipedriveLeadboosterConfig = { base : 'leadbooster-chat.pipedrive.com', companyId : 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version : 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster existe déjà') } else { w.LeadBooster = { q : [], on : function (n, h) { this.q.push({ t : 'o', n : n, h : h }) }, trigger : function (n) { this.q.push({ t : 't', n : n }) }, } } })() Quelques astuces pour accélérer votre application JavaScript - The Codest
The Codest
  • A propos de nous
  • Services
    • Développement de logiciels
      • Développement frontal
      • Développement backend
    • Staff Augmentation
      • Développeurs frontaux
      • Développeurs backend
      • Ingénieurs des données
      • Ingénieurs en informatique dématérialisée
      • Ingénieurs AQ
      • Autres
    • Conseil consultatif
      • Audit et conseil
  • Industries
    • Fintech et banque
    • E-commerce
    • Adtech
    • Santé (Healthtech)
    • Fabrication
    • Logistique
    • Automobile
    • IOT
  • Valeur pour
    • CEO
    • CTO
    • Gestionnaire des livraisons
  • Notre équipe
  • Études de cas
  • Savoir comment
    • Blog
    • Rencontres
    • Webinaires
    • Ressources
Carrières Prendre contact
  • A propos de nous
  • Services
    • Développement de logiciels
      • Développement frontal
      • Développement backend
    • Staff Augmentation
      • Développeurs frontaux
      • Développeurs backend
      • Ingénieurs des données
      • Ingénieurs en informatique dématérialisée
      • Ingénieurs AQ
      • Autres
    • Conseil consultatif
      • Audit et conseil
  • Valeur pour
    • CEO
    • CTO
    • Gestionnaire des livraisons
  • Notre équipe
  • Études de cas
  • Savoir comment
    • Blog
    • Rencontres
    • Webinaires
    • Ressources
Carrières Prendre contact
Flèche arrière RETOUR
2020-11-25
Développement de logiciels

Quelques astuces pour accélérer votre application JavaScript

The Codest

Bartosz Slysz

Software Engineer

Avec les progrès de la technologie des navigateurs, les applications web ont commencé à transférer de plus en plus de logique vers le front-end, soulageant ainsi le serveur et réduisant le nombre d'opérations qu'il doit effectuer. Dans les applications CRUD de base, le rôle du serveur se résume à l'autorisation, à la validation, à la communication avec les bases de données et à la logique commerciale requise. Le reste de la logique des données peut être facilement géré par le code responsable de la représentation de l'application du côté de l'interface utilisateur.

Dans cet article, j'essaierai de vous montrer quelques exemples et modèles qui vous aideront à garder nos code efficace, soigné et rapide.

Avant d'aller plus loin dans les exemples spécifiques - dans cet article, je voudrais me concentrer uniquement sur des exemples qui, à mon avis, peuvent affecter la vitesse de l'application d'une manière surprenante. Toutefois, cela ne signifie pas que l'utilisation de solutions plus rapides est le meilleur choix dans tous les cas possibles. Les conseils ci-dessous devraient plutôt être considérés comme des éléments à prendre en compte lorsque notre application fonctionne lentement, par exemple dans les produits qui nécessitent un rendu de jeu ou des graphiques plus avancés sur le canevas, des opérations vidéo ou des activités que vous souhaitez synchroniser en temps réel dès que possible.

Tout d'abord, les méthodes Array.prototype

Nous basons une grande partie de la logique de l'application sur les tableaux - leur mise en correspondance, leur tri, leur filtrage, la somme des éléments, etc. De manière simple, transparente et naturelle, nous utilisons leurs méthodes intégrées qui nous permettent simplement d'effectuer divers types de calculs, de regroupements, etc. Elles fonctionnent de manière similaire dans chaque cas - en tant qu'argument, nous passons une fonction où, dans la plupart des cas, la valeur de l'élément, l'index et le tableau sont poussés à tour de rôle au cours de chaque itération. La fonction spécifiée est exécutée pour chaque élément du tableau et le résultat est interprété différemment selon la méthode. Je ne m'étendrai pas sur les méthodes de Array.prototype car je veux me concentrer sur la raison pour laquelle il fonctionne lentement dans un grand nombre de cas.

Les méthodes de tableau sont lentes car elles exécutent une fonction pour chaque élément. Une fonction appelée du point de vue du moteur doit préparer un nouvel appel, fournir la portée appropriée et un grand nombre d'autres dépendances, ce qui rend le processus beaucoup plus long que la répétition d'un bloc de code spécifique dans une portée spécifique. Ceci est probablement suffisant pour nous permettre de comprendre l'exemple suivant :

(() => {
const randomArray = [...Array(1E6).keys()].map(() => ({ value : Math.random() })) ;

console.time('Somme par réduction') ;
const reduceSum = randomArray
    .map(({ valeur }) => valeur)
    .reduce((a, b)) => a + b) ;
console.timeEnd('Somme par réduction') ;

console.time('Somme par la boucle for') ;
let forSum = randomArray[0].value ;
for (let index = 1 ; index < randomArray.length ; index++) {
    forSum += randomArray[index].value ;
}
console.timeEnd('Somme par boucle for') ;

console.log(reduceSum === forSum) ;

})() ;

Je sais que ce test n'est pas aussi fiable que les benchmarks (nous y reviendrons plus tard), mais il déclenche un signal d'alarme. Pour un cas aléatoire sur mon ordinateur, il s'avère que le code avec la boucle for peut être environ 50 fois plus rapide si on le compare au mappage puis à la réduction d'éléments qui permettent d'obtenir le même effet ! Il s'agit d'opérer sur un objet étrange créé uniquement pour atteindre un objectif de calcul spécifique. Créons donc quelque chose de plus légitime pour être objectif à propos des méthodes Array :

(() => {
const randomArray = [...Array(1E6).keys()].map(() => ({ value : Math.random() })) ;

console.time('Somme par réduction') ;
const reduceSum = randomArray
    .reduce((a, b) => ({ value : a.value + b.value })).value
console.timeEnd('Somme par réduction') ;

console.time('Somme par la boucle for') ;
let forSum = randomArray[0].value ;
for (let index = 1 ; index < randomArray.length ; index++) {
    forSum += randomArray[index].value ;
}
console.timeEnd('Somme par boucle for') ;

console.log(reduceSum === forSum) ;

})() ;

Je sais que ce test n'est pas aussi fiable que les benchmarks (nous y reviendrons plus tard), mais il déclenche un signal d'alarme. Pour un cas aléatoire sur mon ordinateur, il s'avère que le code avec la boucle for peut être environ 50 fois plus rapide si on le compare au mappage puis à la réduction des éléments qui permettent d'obtenir le même effet ! En effet, pour obtenir la somme dans ce cas particulier à l'aide de la méthode de réduction, il faut mapper le tableau pour les valeurs pures que nous voulons résumer. Créons donc quelque chose de plus légitime pour être objectif à propos des méthodes Array :

(() => {
const randomArray = [...Array(1E6).keys()].map(() => ({ value : Math.random() })) ;

console.time('Somme par réduction') ;
const reduceSum = randomArray
    .reduce((a, b) => ({ value : a.value + b.value })).value
console.timeEnd('Somme par réduction') ;

console.time('Somme par la boucle for') ;
let forSum = randomArray[0].value ;
for (let index = 1 ; index < randomArray.length ; index++) {
    forSum += randomArray[index].value ;
}
console.timeEnd('Somme par boucle for') ;

console.log(reduceSum === forSum) ;

})() ;

Et il s'avère que l'augmentation de 50x est tombée à 4x. Je vous prie de m'excuser si vous êtes déçu ! Pour rester objectif jusqu'au bout, analysons à nouveau les deux codes. Tout d'abord, des différences d'apparence anodine ont doublé la baisse de notre complexité informatique théorique ; au lieu de mettre en correspondance puis d'additionner des éléments purs, nous opérons toujours sur des objets et un champ spécifique, pour finalement les extraire et obtenir la somme qui nous intéresse. Le problème survient lorsqu'un autre programmeur jette un coup d'œil au code - alors, comparé aux codes présentés plus haut, ce dernier perd son abstraction à un moment donné.

C'est parce que depuis la deuxième opération que nous opérons sur un objet étrange, avec le champ qui nous intéresse et le deuxième objet, standard, du tableau itéré. Je ne sais pas ce que vous en pensez, mais de mon point de vue, dans le deuxième exemple de code, la logique de la boucle for est beaucoup plus claire et plus intentionnelle que cette réduction à l'aspect étrange. Et pourtant, même si ce n'est plus le mythique 50, c'est toujours 4 fois plus rapide en termes de temps de calcul ! Comme chaque milliseconde est précieuse, le choix dans ce cas est simple.

L'exemple le plus surprenant

La deuxième chose que je voulais comparer concerne la méthode Math.max ou, plus précisément, l'insertion d'un million d'éléments et l'extraction des plus grands et des plus petits. J'ai préparé le code, les méthodes de mesure du temps également, puis je lance le code et j'obtiens une erreur très étrange - la taille de la pile est dépassée. Voici le code :

(() => {
const randomValues = [...Array(1E6).keys()].map(() => Math.round(Math.random() * 1E6) - 5E5) ;

console.time('Math.max with ES6 spread operator') ;
const maxBySpread = Math.max(...randomValues) ;
console.timeEnd('Math.max avec l'opérateur d'étalement ES6') ;

console.time('Math.max avec boucle for') ;
let maxByFor = randomValues[0] ;
for (let index = 1 ; index  maxByFor) {
        maxByFor = randomValues[index] ;
    }
}
console.timeEnd('Math.max avec boucle for') ;

console.log(maxByFor === maxBySpread) ;

})() ;
(() => {
const randomValues = [...Array(1E6).keys()].map(() => Math.round(Math.random() * 1E6) - 5E5) ;

console.time('Math.max with ES6 spread operator') ;
const maxBySpread = Math.max(...randomValues) ;
console.timeEnd('Math.max avec l'opérateur d'étalement ES6') ;

console.time('Math.max avec boucle for') ;
let maxByFor = randomValues[0] ;
for (let index = 1 ; index  maxByFor) {
        maxByFor = randomValues[index] ;
    }
}
console.timeEnd('Math.max avec boucle for') ;

console.log(maxByFor === maxBySpread) ;

})() ;

Il s'avère que les méthodes natives utilisent la récursivité, qui dans la v8 est limitée par les piles d'appels et dont le nombre dépend de l'environnement. C'est quelque chose qui m'a beaucoup surpris, mais qui amène une conclusion : la méthode native est plus rapide, tant que notre tableau ne dépasse pas un certain nombre magique d'éléments, qui dans mon cas s'est avéré être 125375. Pour ce nombre d'éléments, le résultat pour était 5 fois plus rapide que la boucle. Cependant, au-delà de ce nombre d'éléments, la boucle for l'emporte définitivement - contrairement à son adversaire, elle nous permet d'obtenir des résultats corrects.

Récursion

Le concept que je souhaite mentionner dans ce paragraphe est la récursivité. Dans l'exemple précédent, nous l'avons vu dans la méthode Math.max et le pliage des arguments, où il s'est avéré qu'il est impossible d'obtenir un résultat pour les appels récursifs dépassant un nombre spécifique en raison de la limitation de la taille de la pile.

Nous allons maintenant voir à quoi ressemble la récursion dans le contexte d'un code écrit en JS, et non avec des méthodes intégrées.La chose la plus classique que nous puissions montrer ici est, bien sûr, la recherche du nième terme de la suite de Fibonacci. Alors, écrivons cela !

(() => {
const fiboIterative = (n) => {
let [a, b] = [0, 1] ;

    for (let i = 0 ; i  {
    if(n < 2) {
        retourne n ;
    }

    return fiboRecursive(n - 2) + fiboRecursive(n - 1) ;
} ;

console.time('Fibonacci sequence by for loop') ;
const resultIterative = fiboIterative(30) ;
console.timeEnd('Séquence de Fibonacci en boucle') ;

console.time('Suite de Fibonacci par récursion') ;
const resultRecursive = fiboRecursive(30) ;
console.timeEnd('Suite de Fibonacci par récursion') ;

console.log(resultRecursive === resultIterative) ;

})() ;

D'accord, dans ce cas particulier de calcul du 30e élément de la séquence sur mon ordinateur, nous obtenons le résultat en un temps environ 200 fois plus court avec l'algorithme itératif.

Il y a cependant une chose qui peut être rectifiée dans l'algorithme récursif - il s'avère qu'il fonctionne beaucoup plus efficacement lorsque nous utilisons une tactique appelée récursion de queue. Cela signifie que nous transmettons le résultat obtenu lors de l'itération précédente en tant qu'argument pour les appels plus profonds. Cela nous permet de réduire le nombre d'appels nécessaires et, par conséquent, d'accélérer le résultat. Corrigeons notre code en conséquence !

(() => {
const fiboIterative = (n) => {
let [a, b] = [0, 1] ;

    for (let i = 0 ; i  {
    if(n === 0) {
        return first ;
    }

    return fiboTailRecursive(n - 1, second, first + second) ;
} ;

console.time('Fibonacci sequence by for loop') ;
const resultIterative = fiboIterative(30) ;
console.timeEnd('Séquence de Fibonacci en boucle') ;

console.time('Séquence de Fibonacci par récursion de queue') ;
const resultRecursive = fiboTailRecursive(30) ;
console.timeEnd('Suite de Fibonacci par récursion de la queue') ;

console.log(resultRecursive === resultIterative) ;

})() ;

Quelque chose que je n'attendais pas s'est produit ici - le résultat de l'algorithme de récursion de queue a été capable de fournir le résultat (calcul du 30ème élément d'une séquence) presque deux fois plus vite que l'algorithme itératif dans certains cas. Je ne sais pas si cela est dû à l'optimisation de la récursivité de la queue de la part de v8 ou au manque d'optimisation de la boucle for pour ce nombre spécifique d'itérations, mais le résultat est sans ambiguïté - la récursivité de la queue l'emporte.

C'est bizarre parce que, essentiellement, la boucle for impose beaucoup moins d'abstraction sur les activités de calcul de niveau inférieur, et on pourrait dire qu'elle est plus proche de l'opération de base de l'ordinateur. Pourtant, les résultats sont indéniables : une récursion intelligemment conçue s'avère plus rapide que l'itération.

Utilisez des instructions d'appel asynchrones aussi souvent que possible

Je voudrais consacrer le dernier paragraphe à un bref rappel sur une méthode d'exécution des opérations qui peut également affecter grandement la vitesse de notre application. Comme vous devez le savoir, JavaScript est un langage à un seul fil qui conserve toutes les opérations avec un mécanisme de boucle d'événements. Il s'agit d'un cycle qui se répète sans cesse et dont toutes les étapes concernent des actions spécifiques dédiées.

Pour que cette boucle soit rapide et que tous les cycles attendent moins longtemps leur tour, tous les éléments doivent être aussi rapides que possible. Évitez d'exécuter de longues opérations sur le thread principal - si quelque chose prend trop de temps, essayez de déplacer ces calculs dans le WebWorker ou de les diviser en parties qui s'exécutent de manière asynchrone. Cela peut ralentir certaines opérations mais améliorer l'ensemble de l'écosystème JS, y compris les opérations d'E/S, telles que la gestion du déplacement de la souris ou des requêtes HTTP en attente.

Résumé

En conclusion, comme nous l'avons déjà mentionné, la chasse aux millisecondes qui peuvent être économisées en sélectionnant un algorithme peut s'avérer insensée dans certains cas. D'un autre côté, négliger ce genre de choses dans des applications qui nécessitent un fonctionnement fluide et des résultats rapides peut s'avérer mortel pour votre application. Dans certains cas, outre la vitesse de l'algorithme, il convient de se poser une question supplémentaire : l'abstraction est-elle exploitée au bon niveau ? Le programmeur qui lira le code pourra-t-il le comprendre sans problème ?

Le seul moyen est de garantir l'équilibre entre les performances, la facilité de mise en œuvre et l'abstraction appropriée, et de s'assurer que l'algorithme fonctionne correctement pour les petites et les grandes quantités de données. La manière d'y parvenir est assez simple : soyez intelligent, envisagez les différents cas lors de la conception de l'algorithme et faites en sorte qu'il se comporte de la manière la plus efficace possible pour les exécutions moyennes. Il est également conseillé de concevoir des tests - assurez-vous que l'algorithme renvoie les informations appropriées pour différentes données, quel que soit son mode de fonctionnement. Veillez à ce que les interfaces soient adéquates - de sorte que les entrées et les sorties des méthodes soient lisibles, claires et reflètent exactement ce qu'elles font.

J'ai mentionné plus tôt que je reviendrai sur la fiabilité de la mesure de la vitesse des algorithmes dans les exemples ci-dessus. Les mesurer avec console.time n'est pas très fiable, mais c'est ce qui reflète le mieux le cas d'utilisation standard. Quoi qu'il en soit, je présente les benchmarks ci-dessous - certains d'entre eux semblent un peu différents d'une exécution unique en raison du fait que les benchmarks répètent simplement une activité donnée à un certain moment et utilisent l'optimisation v8 pour les boucles.

  • https://jsben.ch/KhAqb - réduction vs boucle for
  • https://jsben.ch/F4kLY - réduction optimisée par rapport à la boucle for
  • https://jsben.ch/MCr6g - Math.max vs for loop
  • https://jsben.ch/A0CJB - fibo récursif vs fibo itératif
  • https://jsben.ch/NFLsl - Fibo récursif vs Fibo itératif 

Et voilà, profitez de votre piratage !

En savoir plus :

Comment améliorer les applications Vue.js ? Quelques conseils pratiques

Comment améliorer les performances de Rails

Comment écrire un code de qualité ?

Articles connexes

Développement de logiciels

Construire des applications web à l'épreuve du temps : les conseils de l'équipe d'experts de The Codest

Découvrez comment The Codest excelle dans la création d'applications web évolutives et interactives à l'aide de technologies de pointe, offrant une expérience utilisateur transparente sur toutes les plateformes. Découvrez comment notre expertise favorise la transformation numérique et la...

LE CODEST
Développement de logiciels

Les 10 premières entreprises de développement de logiciels basées en Lettonie

Découvrez les principales sociétés de développement de logiciels en Lettonie et leurs solutions innovantes dans notre dernier article. Découvrez comment ces leaders de la technologie peuvent vous aider à développer votre entreprise.

thecodest
Solutions pour les entreprises et les grandes entreprises

L'essentiel du développement de logiciels Java : Un guide pour une externalisation réussie

Explorez ce guide essentiel sur le développement réussi de logiciels Java outsourcing pour améliorer l'efficacité, accéder à l'expertise et assurer la réussite des projets avec The Codest.

thecodest
Développement de logiciels

Le guide ultime de l'externalisation en Pologne

L'essor de outsourcing en Pologne est dû aux progrès économiques, éducatifs et technologiques, qui favorisent la croissance des technologies de l'information et un climat propice aux entreprises.

TheCodest
Solutions pour les entreprises et les grandes entreprises

Le guide complet des outils et techniques d'audit informatique

Les audits informatiques garantissent la sécurité, l'efficacité et la conformité des systèmes. Pour en savoir plus sur leur importance, lisez l'article complet.

The Codest
Jakub Jakubowicz CTO & Co-Fondateur

Abonnez-vous à notre base de connaissances et restez au courant de l'expertise du secteur des technologies de l'information.

    A propos de nous

    The Codest - Entreprise internationale de développement de logiciels avec des centres technologiques en Pologne.

    Royaume-Uni - Siège

    • Bureau 303B, 182-184 High Street North E6 2JA
      Londres, Angleterre

    Pologne - Les pôles technologiques locaux

    • Parc de bureaux Fabryczna, Aleja
      Pokoju 18, 31-564 Kraków
    • Brain Embassy, Konstruktorska
      11, 02-673 Varsovie, Pologne

      The Codest

    • Accueil
    • A propos de nous
    • Services
    • Études de cas
    • Savoir comment
    • Carrières
    • Dictionnaire

      Services

    • Conseil consultatif
    • Développement de logiciels
    • Développement backend
    • Développement frontal
    • Staff Augmentation
    • Développeurs backend
    • Ingénieurs en informatique dématérialisée
    • Ingénieurs des données
    • Autres
    • Ingénieurs AQ

      Ressources

    • Faits et mythes concernant la coopération avec un partenaire externe de développement de logiciels
    • Des États-Unis à l'Europe : Pourquoi les startups américaines décident-elles de se délocaliser en Europe ?
    • Comparaison des pôles de développement Tech Offshore : Tech Offshore Europe (Pologne), ASEAN (Philippines), Eurasie (Turquie)
    • Quels sont les principaux défis des CTO et des DSI ?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Conditions d'utilisation du site web

    Copyright © 2025 par The Codest. Tous droits réservés.

    fr_FRFrench
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish pl_PLPolish arArabic it_ITItalian jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek fr_FRFrench