front-end

Przerywanie pobierania danych - Quiz z AbortControllerem - #funWithJavascript

Dzień dobry! Jak się macie? U mnie wspaniale, chociaż dosyć pracowicie, mam nadzieje, że ten rok będzie pełen sukcesów.

Bardzo mi miło, że ze mną tutaj jesteście! Coraz więcej osób mnie czyta, dziękuję za ten czas, mam nadzieje, że nie jest zmarnowany!

Dzisiaj przychodzę do Was z bardzo ciekawym tematem, który postanowiłam ogarnąć w weekend.

Fetch

// implementacja syntax-sugar
const response = await fetch('https://pokeapi.co/api/v2/pokemon');
const json = await response.json();
console.log(json));

// implementacja sugar-free
fetch('https://pokeapi.co/api/v2/pokemon')
.then((response) => response.json())
    .then((json) =>  {
        console.log(json); 
    }); 

Fetch to natywna funkcja umożliwiająca asynchroniczne pobieranie danych, swojego rodzaju alternatywa dla XMLHttpRequest. Istnieje pomiędzy nimi kilka różnic w które polecam się zagłębić, jednak kluczową informacją jest, że fetch zwraca Promise'a.

Co oznacza, że los fetch'a nie jest przekreślony, tym bardziej jeśli skorzystamy z AbortAPI.

AbortAPI

Sprawa jest prosta: przeglądarka udostępnia narzędzia, które umożliwiają przerwanie dowolnego requestu z DOM, a ja pokażę wam się tego używa.

Przykład będzie bardzo życiowy: chcemy pobrać dane, które są tak dynamiczne że czekanie na nie powyżej sekundy mija się z celem, a nawet szkodzi naszej aplikacji.

To co? Ruszamy do kodowania!

Przykładowe wywołanie poniżej, oszczędziłam Wam catch'a, ale nie powinnam!

const response = SolutionFetch.fetch('https://pokeapi.co/api/v2/pokemon', 6000)
    .then((response) => response.json())
    .then((json) => {
        console.log(json);
        // pokaż tylko wartościowe dane
    })
    .catch(() => {});

A teraz wrapper na natywnego fetcha:

class SolutionFetch {
    static fetch(url, timeout, configObj = {}) {
        const controller = new AbortController();
        const signal = controller.signal;
        let timeoutReference = null;
        
        return new Promise((resolve, reject) => {
        
            // rejestrujemy timeouta
            timeoutReference = setTimeout(() => {
                console.log('ABORT');
                controller.abort();
                return reject();
            }, timeout);

            // fetchujemy prawdziwe dane
            fetch(url, { signal }).then((response) => {
                clearTimeout(timeoutReference);
                console.log('OK');
                return resolve(response);
            }).catch((error) => {
                console.log('NOPE');
                clearTimeout(timeoutReference);
                return reject(error);
            });
        });
    }
}

Jak to działa? Gdy utworzymy controller i podamy jego sygnał do fetcha, możemy go przerwać w dowolnym momencie! Kluczowe jest tutaj controller.abort().

To teraz szybki quiz:

1) Co będzie w konsoli jeśli wywołamy te funkcję z dużym timeoutem (czyli wartością po której chcemy zrezygnować z fetcha, w tym przykładzie 6000ms)?

2) A co jeśli timeout będzie równy 1ms? Spójrzcie do góry na kod i zastanówcie się.

3) Co się zmieni jeśli wykomentujemy 12 linijkę ( controller.abort(); ) i czas będzie równy 1ms ?

4) Co się stanie jeśli nie wyclearujemy SetTimeout'a, gdy czas będzie długi (na przykład 1000ms)?

I jak? Proste pytania czy wymagają nieco zastanowienia?

Full focus at a coffee shop
Photo by Tim Gouw / Unsplash

Jesteście pewni, że chcecie znać odpowiedzi? Jeśli tak to scrollujcie dalej!

Round wooden coin with eye detail and word, text "yes" printed on it, held in the palm of a woman's hand.
Photo by Jen Theodore / Unsplash

A teraz wyniki:

1) Wyświetli się zarówno "OK" jak i odpowiedź z serwera z listą pokemonów.

2) Wyświetli się "ABORT" i "NOPE", a request nie zostanie wysłany. Można to zobaczyć w zakładce "Sieć"/"Network"

3) Wyświetli się "ABORT" i "OK", request zostanie wysłany.

4) Wyświetli się "OK", potem pojawi się odpowiedź z serwera, a po chwili "ABORT". Raz zresolvowanego promise'a nie da się rejectować, więc reject w setTimeoutcie nie będzie miał znaczenia.

Podsumowanie

Mam nadzieje, że się podobało :) Staram się cały czas na nowo odkrywać Javascript, a pisanie #funWithJavascript to najlepsza okazja!

Pochwalcie się wynikami? Udało Wam się rozwiązać mój mini quiz czy gdzieś się pomyliliście?

Pozdrawiam serdecznie i bardzo dziękuję za wszystkie polubienia, udostępnienia oraz miłe słowa!

Kamila

Dziękuję za poświęcony czas, będzie mi bardzo miło jak zostawisz komentarz :)