Throttling jest kolejnym sposobem na optymalizację aplikacji. Bardzo często throttling jest opisywany wraz z debounce (o którym pisałem tutaj – Debounce function), ponieważ oba te mechanizmy pozwalają na wywołanie funkcji, po wystąpieniu określonych warunków. O różnicach napiszę później, chociaż po przeczytaniu artykułów o throttling oraz debounce, wszystko powinno być jasne.

Jak działa throttling?

Korzystając z throttling możemy wywołać funkcję tylko raz, w określonym przez nas interwale czasowym.
Żeby lepiej zobrazować przypadek użycia, wyobraźmy sobie, że chcemy wywołać jakieś zdarzenie podczas ruchu myszką. Jeżeli wywołamy funkcję przy każdym ruchu, to w praktyce otrzymamy bardzo dużą liczbę wywołań funkcji. Do optymalizacji wywołań idealnie nadaje się throttling, co pokażę w poniższych przykładach.

Przejdźmy do przykładu…

Skorzystamy z create-react-app, a następnie z biblioteki lodash, dodając ją do projektu poprzez komendę:

npm i --save lodash

Przykład onMouseMove bez throttling

Nasz przypadek użycia będzie obejmował wyświetlenie w konsoli położenia kursora po ruchu myszką w ramach elementu <div>

class App extends React.Component {
    mouseMove = (event) => {
        console.log(new Date().toISOString(), ' clientX:', event.clientX, 'clientY:', event.clientY);
    }

    render() {
        return (
            <div onMouseMove={this.mouseMove}>
                Example
            </div>
        );
    }
}

Jak widać w konsoli, liczba wywołań funkcji jest olbrzymia. Każdy najmniejszy ruch myszką powoduje wywołanie funkcji:

2020-12-20T16:40:28.490Z  clientX: 38 clientY: 11
2020-12-20T16:40:28.497Z  clientX: 55 clientY: 11
2020-12-20T16:40:28.504Z  clientX: 68 clientY: 11
2020-12-20T16:40:28.512Z  clientX: 78 clientY: 11
2020-12-20T16:40:28.520Z  clientX: 84 clientY: 11

Przykład ten jest bardzo prosty, ale w sytuacji gdy wymagalibyśmy jakiś obliczeń i np. uderzenia do API, aby wysłać dany wynik, zwykłe onMouseMove bez dodatkowych ograniczeń, mogłoby się nie sprawdzić.

Skorzystajmy zatem z mechanizmu throttling, aby zoptymalizować liczbę wywołań.

Przykład onMouseMove z throttling

import {throttle} from 'lodash';

class App extends React.Component {
    constructor(props) {
        super(props);
        this.throttledMouseMove = throttle(this._mouseMove, 5000);
    }

    _mouseMove = (event) => {
        console.log(new Date().toISOString(), ' clientX:', event.clientX, 'clientY:', event.clientY);
    }

    render() {
        return (
            <div onMouseMove={this.throttledMouseMove}>
                Example
            </div>
        );
    }
}

Co zmieniło się w komponencie? Przede wszystkim zaimportowaliśmy throttle z biblioteki lodash. Dodatkowo, w konstruktorze zdefiniowaliśmy obsługę mechanizmu throttle. Ustawienienia powodują wywołanie funkcji raz na 5 sekund (5000 ms).

this.throttledMouseMove = throttle(this._mouseMove, 5000);

Tym prostym sposobem ograniczyliśmy liczbę wywołań funkcji do jednej w ciągu 5 sekund (parametr).
Widać to też w konsoli, gdy nieustannie ruszamy myszką w ramach obszaru <div>.

2020-12-20T16:17:23.721Z  clientX: 60 clientY: 12
2020-12-20T16:17:28.724Z  clientX: 37 clientY: 10
2020-12-20T16:17:33.724Z  clientX: 19 clientY: 14
2020-12-20T16:17:38.765Z  clientX: 65 clientY: 6
2020-12-20T16:17:43.765Z  clientX: 108 clientY: 12

Throttling i debounce – różnice

Zmieńmy jeszcze wywołanie throttle na debounce i zobaczmy, jak teraz zachowuje się komponent.

import React from 'react';
import {debounce} from 'lodash';

class App extends React.Component {
    constructor(props) {
        super(props);
        this.debounceMouseMove = debounce(this._mouseMove, 5000);
    }

    _mouseMove = (event) => {
        console.log(new Date().toISOString(), ' clientX:', event.clientX, 'clientY:', event.clientY);
    }

    render() {
        return (
            <div onMouseMove={this.debounceMouseMove}>
                Example
            </div>
        );
    }
}

Jak przetestować działanie? Wystarczy poruszać myszką przez kilkanaście sekund, następnie przerwać ruchy myszką. W ten sposób dopiero 5 sekund po zaprzestaniu ruchów myszką w konsoli otrzymamy wynik console.log(). W skrócie, debounce uruchamia się raz, X milisekund po zakończeniu zdarzenia. Throttle z kolei uruchamia się raz, w ramach zdefiniowanego interwału czasowego. Myślę, że te proste przykłady pozwolą dobrać odpowiednie użycie funkcję w Waszych aplikacjach.

Źródła:
Obraz: https://unsplash.com/photos/RSsqjpezn6o
Lodash: https://www.npmjs.com/package/lodash

Create react app: https://pl.reactjs.org/docs/create-a-new-react-app.html