<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" ><channel><title>Kévin Subileau &#187; optimisation</title> <atom:link href="http://www.kevinsubileau.fr/tag/optimisation/feed" rel="self" type="application/rss+xml" /><link>http://www.kevinsubileau.fr</link> <description>Espace personnel</description> <lastBuildDate>Sun, 02 Feb 2020 15:18:58 +0000</lastBuildDate> <language>fr-FR</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <item><title>Debounce et throttle : limiter les appels successifs à une fonction Javascript</title><link>http://www.kevinsubileau.fr/informatique/boite-a-code/php-html-css/javascript-debounce-throttle-reduire-appels-fonction.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=rss</link> <comments>http://www.kevinsubileau.fr/informatique/boite-a-code/php-html-css/javascript-debounce-throttle-reduire-appels-fonction.html#comments</comments> <pubDate>Mon, 08 Dec 2014 20:10:41 +0000</pubDate> <dc:creator>Kévin Subileau</dc:creator> <category><![CDATA[PHP, HTML et CSS]]></category> <category><![CDATA[optimisation]]></category><guid isPermaLink="false">http://www.kevinsubileau.fr/?p=1306</guid> <description><![CDATA[Les fonctions debounce et throttle sont deux techniques très efficaces pour optimiser une application Web, en évitant les appels trop fréquents à une fonction Javascript en réaction à certains événements par exemple. <a href="http://www.kevinsubileau.fr/informatique/boite-a-code/php-html-css/javascript-debounce-throttle-reduire-appels-fonction.html">Lire la Suite <span class="meta-nav">&#8594;</span></a>]]></description> <content:encoded><![CDATA[<p>Lorsque l'on développe une application Web réagissant à certains <strong>évènements en Javascript</strong>, comme le redimensionnement de la fenêtre, le mouvement de la souris ou la frappe au clavier, les fonctions rattachées à ces événements risquent d'être appelées très fréquemment. Si le code contenu dans ces fonctions est coûteux en ressources, cela peut ralentir considérablement l'application Web.</p><p>Pour <strong>améliorer les performances</strong>, ou plutôt ne pas les plomber, il existe deux techniques simples, appelées <strong><em>debounce</em></strong> et <strong><em>throttle</em></strong>. Toutes deux permettent de <strong>réduire le nombre d'appels</strong> à une fonction <strong>dans un intervalle de temps donné</strong>. En effet, il est souvent inutile d'exécuter ces fonctions à chaque fois que l’événement est déclenché.</p><p>Imaginons par exemple que l'on souhaite synchroniser sur le serveur, via des appels AJAX, le contenu d'un champ de saisie au fur et à mesure que l'utilisateur tape le texte (comme le fait Google Docs par exemple). Dans ce cas, plutôt que de solliciter le serveur à chaque caractère saisi ou inutilement lorsque le texte reste inchangé, il peut être plus judicieux de sauvegarder le texte toutes les 5 secondes tant que l'utilisateur le modifie, et de ne plus faire de requêtes lorsque que le texte reste inchangé. Et c'est exactement ce que la fonction <em>throttle</em> permet de faire.</p><p>Cependant, bien que ces deux techniques soit assez similaires, il existe une <strong>différence fondamentale</strong> dans leurs fonctionnements qu'il est important de bien comprendre pour les utiliser correctement.</p><p>Je vais donc vous présenter ces deux méthodes l'une après l'autre, en vous expliquant leurs <strong>fonctionnements </strong>et en vous donnant le <strong>code Javascript</strong> nécessaire pour les mettre en place.<br /> <span id="more-1306"></span></p><h1><em>Debounce</em></h1><p>Commençons par la fonction <em>debounce</em>. Cette technique a pour but de <strong>n'appeler qu'une seule fois une méthode</strong> au début ou à la fin d'une succession de déclenchements d'un événement. Voici tout d'abord le <strong>code source</strong> de la fonction en question :</p><pre class="gutter: true; brush: javascript;">/**
 * Retourne une fonction qui, tant qu&#039;elle continue à être invoquée,
 * ne sera pas exécutée. La fonction ne sera exécutée que lorsque
 * l&#039;on cessera de l&#039;appeler pendant plus de N millisecondes.
 * Si le paramètre `immediate` vaut vrai, alors la fonction 
 * sera exécutée au premier appel au lieu du dernier.
 * Paramètres :
 *  - func : la fonction à `debouncer`
 *  - wait : le nombre de millisecondes (N) à attendre avant 
 *           d&#039;appeler func()
 *  - immediate (optionnel) : Appeler func() à la première invocation
 *                            au lieu de la dernière (Faux par défaut)
 *  - context (optionnel) : le contexte dans lequel appeler func()
 *                          (this par défaut)
 */
function debounce(func, wait, immediate, context) {
    var result;
    var timeout = null;
    return function() {
        var ctx = context || this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) result = func.apply(ctx, args);
        };
        var callNow = immediate &amp;&amp; !timeout;
        // Tant que la fonction est appelée, on reset le timeout.
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) result = func.apply(ctx, args);
        return result;
    };
}</pre><p>Pour mieux comprendre son fonctionnement, regardons le schéma ci dessous :</p><div id="attachment_1396" class="wp-caption aligncenter" style="width: 751px"><img class="size-full wp-image-1396" alt="js-debounce" src="http://www.kevinsubileau.fr/wp-content/uploads/2014/11/js-debounce.png" width="741" height="210" /><p class="wp-caption-text">Chronogramme de la fonction <em>debounce</em></p></div><p>Sur la première ligne de cette figure, chaque rectangle bleu représente <strong>un déclenchement de l’événement</strong> écouté (<em>keydown</em> par exemple). Lorsque deux déclenchements sont espacés par une <strong>durée inférieure à une valeur prédéfinie</strong> (paramètre <em>wait</em>), on va dire qu'ils appartiennent à <strong>une même séquence</strong>. Si la durée est supérieure, alors cela marque le début d'une nouvelle séquence.</p><p>Sur la deuxième ligne, les rectangles verts représentent les exécutions de la fonction "débouncé" (paramètre <em>func</em>). On remarque alors que la fonction <em>debounce</em> permet d'<strong>attendre la fin d'une séquence d'évènement</strong> pour effectuer le traitement associé. Si maintenant on reprend l'exemple de la synchronisation d'un champ de texte, cela signifie que l'appel AJAX sera effectué uniquement lorsque l'utilisateur cessera de modifier le contenu pendant une durée supérieure à <em>wait</em>.</p><p>Par ailleurs, dans certains cas, il peut être plus intéressant d'exécuter le traitement <strong>au début de la séquence</strong> plutôt qu'à la fin (comme le représente les rectangles violets). Cela est possible simplement en définissant le <strong>paramètre optionnel <em>immediate</em></strong> à <em>true</em>.</p><p>L'utilisation concrète de cette fonction est assez simple. Voici un exemple :</p><pre class="gutter: true; brush: javascript; highlight: [8];">function traiterEvenement() {
	// Fonction de traitement de l&#039;évenement
        // (sauvegarde par appel AJAX par exemple)
}

// On limite l’exécution de la fonction traiterEvenement à une fois
// toutes les 5000 millisecondes (5s) au maximum.
var traiterEvenementDebounce = debounce(traiterEvenement, 5000);

// On s&#039;enregistre sur l’événement de saisie clavier
document.querySelectorAll(&#039;#field&#039;)
        .addEventListener(&#039;keydown&#039;, traiterEvenementDebounce);</pre><p>Cependant, ce mode de fonctionnement <strong>peut parfois poser problème</strong>. Ici par exemple, si l'utilisateur tape son texte en continu pendant plusieurs minutes, il ne sera pas sauvegardé sur le serveur puisque la séquence ne se termine pas. Selon les cas, cela peut être souhaitable ou gênant, c'est à vous de voir. Pour éviter cela, il y a deux solutions :</p><ul><li>Soit <strong>réduire l'intervalle</strong> entre les séquences (de 5s à 500ms par exemple), en se disant que l'utilisateur ne pourra pas taper longtemps sur son clavier sans jamais s'arrêter moins de 500ms. C'est plutôt vrai, mais du coup on augmente potentiellement le nombre d’exécutions, donc ça risque d'être plus lent et ça perd un peu de son intérêt...</li><li>Soit <strong>utiliser la fonction <em>throttle</em></strong>, qui propose un fonctionnement un peu différent.</li></ul><h1><em>Throttle</em></h1><p>Parlons donc maintenant de la fonction <em>throttle</em>. Ici, comme le montre le chronogramme ci-dessous, on va exécuter la fonction de traitement de l'événement <strong>de manière périodique</strong> durant la séquence de déclenchements.</p><div id="attachment_1397" class="wp-caption aligncenter" style="width: 785px"><img class="size-full wp-image-1397" alt="js-throttle" src="http://www.kevinsubileau.fr/wp-content/uploads/2014/11/js-throttle.png" width="775" height="159" /><p class="wp-caption-text">Chronogramme de la fonction <em>throttle</em></p></div><p>Sur cette figure, les rectangles de couleur de la deuxième ligne représentes les exécutions de la fonction <em>func</em>. Attention, par défaut, seuls les rectangles violets sont effectivement exécutés. Pour inclure également les exécutions en début et/ou en fin de séquence (rectangles jaunes et verts), il faut définir respectivement les paramètres <em>leading</em> et/ou <em>trailing</em> à <em>true</em>.</p><p>Si on applique cette fonction à notre exemple, cela signifie que le texte du champ de saisie sera sauvegardé sur le serveur par un appel AJAX toutes les 5 secondes par exemple (paramètre <em>wait</em>), et ce tant que l'utilisateur change le contenu. Et si le texte n'est pas modifié, aucun appel n'est effectué. Ce comportement est donc idéal dans ce cas, puisqu'on limite le nombre d'appels au strict nécessaire pour ne pas risquer une perte de données.</p><p>Voici donc maintenant le code de la fonction <em>throttle</em> :</p><pre class="gutter: true; brush: javascript;">/**
 * Retourne une fonction qui, tant qu&#039;elle est appelée,
 * n&#039;est exécutée au plus qu&#039;une fois toutes les N millisecondes.
 * Paramètres :
 *  - func : la fonction à contrôler
 *  - wait : le nombre de millisecondes (période N) à attendre avant 
 *           de pouvoir exécuter à nouveau la function func()
 *  - leading (optionnel) : Appeler également func() à la première
 *                          invocation (Faux par défaut)
 *  - trailing (optionnel) : Appeler également func() à la dernière
 *                           invocation (Faux par défaut)
 *  - context (optionnel) : le contexte dans lequel appeler func()
 *                          (this par défaut)
 */
function throttle(func, wait, leading, trailing, context) {
    var ctx, args, result;
    var timeout = null;
    var previous = 0;
    var later = function() {
        previous = new Date;
        timeout = null;
        result = func.apply(ctx, args);
    };
    return function() {
        var now = new Date;
        if (!previous &amp;&amp; !leading) previous = now;
        var remaining = wait - (now - previous);
        ctx = context || this;
        args = arguments;
        // Si la période d&#039;attente est écoulée
        if (remaining &lt;= 0) {
            // Réinitialiser les compteurs
            clearTimeout(timeout);
            timeout = null;
            // Enregistrer le moment du dernier appel
            previous = now;
            // Appeler la fonction
            result = func.apply(ctx, args);
        } else if (!timeout &amp;&amp; trailing) {
            // Sinon on s’endort pendant le temps restant
            timeout = setTimeout(later, remaining);
        }
        return result;
    };
};</pre><p>L'utilisation de cette fonction est semblable à la fonction <em>debounce</em>, hormis les arguments optionnels qui sont différents. Voici un exemple :</p><pre class="gutter: true; brush: javascript;  highlight: [7];">function traiterEvenement() {
	// Fonction de traitement de l&#039;évenement
        // (sauvegarde par appel AJAX par exemple)
}

// Ici il est utile d&#039;activer le `trailing` pour traiter le dernier événement
var traiterEvenementThrottle = throttle(traiterEvenement, 5000, false, true);

// On s&#039;enregistre sur l’événement de saisie clavier
document.querySelectorAll(&#039;#field&#039;)
        .addEventListener(&#039;keydown&#039;, traiterEvenementThrottle);</pre><p>Les applications possibles de ces fonctions sont très nombreuses. Ici, j'ai pris l'exemple d'une fonctionnalité d'enregistrement automatique. Mais on peut aussi les utiliser pour repositionner des éléments lors du redimensionnement de la fenêtre, enregistrer des événements sur Piwik sans trop de répétitions, ...</p><p>Notez que <strong>certaines bibliothèques Javascript</strong>, comme <a href="http://lodash.com/">Lodash</a> ou <a href="http://underscorejs.org/#debounce">UnderscoreJS</a>, <strong>fournissent directement ces fonctions</strong>. Les implémentations que je vous propose ci-dessus en sont d'ailleurs inspirées. Inutile donc de les dupliquer si vous utilisez déjà une de ces bibliothèques pour votre application Web.</p><p>Enfin, bien que je parle ici de Web et de Javascript, je pense qu'il est tout à fait envisageable de reprendre le principe pour d'autres types de développement, y compris des applications bureaux en C++, C# ou Java par exemple. Étant donné la singularité de la syntaxe Javascript, il faudra en revanche revoir entièrement l'implémentation...</p><p>Voilà, j'espère que cet article vous aura aidé à comprendre l'utilité et le fonctionnement de ces fonctions. Si vous avez des questions, n'hésitez pas à me les poser en commentaire <img src='http://www.kevinsubileau.fr/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> .</p><p><strong>Maj 12/08/2015</strong> :  En complément de cet article, je vous conseille de voir également ce <a href="http://www.grafikart.fr/tutoriels/javascript/debounce-throttle-642">tutoriel vidéo</a> que l'ami Grafikart a publié ce jour.</p> ]]></content:encoded> <wfw:commentRss>http://www.kevinsubileau.fr/informatique/boite-a-code/php-html-css/javascript-debounce-throttle-reduire-appels-fonction.html/feed</wfw:commentRss> <slash:comments>1</slash:comments> </item> </channel> </rss>
<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk: basic
Page Caching using disk: enhanced
Database Caching using disk: basic

Served from: www.kevinsubileau.fr @ 2026-04-06 06:17:41 -->