Async vs Defer - Wybierz dobrze i przyspiesz swoją stronę!

Wykres porównuje ładowanie skryptów: standardowe, defer i async. Różnice w parsowaniu, pobieraniu i wykonaniu.

Napisano przez

Jacek Zając

Opublikowano

7 maj 2026

Spis treści

Gdy strona ładuje dużo JavaScriptu, różnica między momentem pobrania pliku a momentem jego wykonania decyduje o tym, czy interfejs pojawi się szybko i czy kod uruchomi się w przewidywalnej kolejności. To praktyczne porównanie defer vs async pokazuje, kiedy przeglądarka wstrzymuje parsowanie HTML, kiedy uruchamia skrypt od razu, a kiedy czeka do końca budowy DOM. Dobrze dobrany atrybut potrafi uprościć kod, przyspieszyć stronę i uniknąć błędów, które na produkcji są wyjątkowo kosztowne.

Co warto zapamiętać na start

  • async pobiera skrypt równolegle i uruchamia go natychmiast po pobraniu, bez gwarancji kolejności.
  • defer pobiera skrypt równolegle, ale wykonuje go dopiero po sparsowaniu HTML, w kolejności występowania.
  • Oba atrybuty mają sens głównie przy zewnętrznych skryptach klasycznych, nie przy inline.
  • DOMContentLoaded czeka na skrypty z defer, ale nie czeka na async.
  • type="module" zachowuje się jak odroczony domyślnie, więc często nie trzeba dodawać defer.

Wizualizacja ładowania skryptów: porównanie `defer` i `async`. Pokazuje, jak przeglądarka parsuje HTML i wykonuje JS.

Najważniejsze różnice w jednym spojrzeniu

Jeśli chcesz porównać te atrybuty bez wchodzenia od razu w szczegóły implementacyjne, najlepiej spojrzeć na trzy elementy: moment pobrania, moment wykonania i kolejność. W praktyce chodzi o to, czy skrypt ma być ważniejszy od HTML, czy raczej ma poczekać, aż dokument będzie gotowy.

Cecha async defer
Pobieranie pliku Równolegle z parsowaniem HTML Równolegle z parsowaniem HTML
Moment wykonania Jak tylko skrypt zostanie pobrany Po zakończeniu parsowania dokumentu, tuż przed DOMContentLoaded
Kolejność uruchomienia Nie jest gwarantowana Jest zachowana zgodnie z kolejnością w dokumencie
Wpływ na DOMContentLoaded Nie blokuje tego wydarzenia Opóźnia je do czasu wykonania skryptu
Najlepsze zastosowanie Skrypty niezależne od DOM i od innych plików Skrypty zależne od DOM albo od innych skryptów

Gdy patrzę na ten zestaw, zwykle upraszczam decyzję do jednego pytania: czy ten kod może uruchomić się w dowolnym momencie, czy musi czekać na strukturę strony. Od odpowiedzi zależy praktycznie cały wybór. W kolejnym kroku rozkładam to na faktyczny przebieg ładowania w przeglądarce.

Jak przeglądarka interpretuje async i defer podczas ładowania

W klasycznym scenariuszu zewnętrzny skrypt bez żadnego atrybutu zatrzymuje parsowanie HTML. To ważne rozróżnienie: przeglądarka nie tylko czeka na plik, ale też przerywa budowanie drzewa DOM, co przy większych zasobach potrafi wyraźnie opóźnić wyświetlenie strony.

  1. Przeglądarka czyta HTML od góry do dołu.
  2. Jeśli natrafi na zwykły zewnętrzny , zatrzymuje parsowanie, pobiera plik i dopiero potem go wykonuje.
  3. Jeśli skrypt ma async, pobieranie odbywa się równolegle, a wykonanie następuje natychmiast po pobraniu, nawet jeśli HTML nie został jeszcze w całości przeanalizowany.
  4. Jeśli skrypt ma defer, pobieranie też odbywa się równolegle, ale wykonanie czeka do końca parsowania dokumentu.
  5. DOMContentLoaded pojawia się dopiero po wykonaniu skryptów odroczonych, ale nie czeka na skrypty asynchroniczne.

W praktyce oznacza to, że async jest bardziej agresywny: daje skryptowi możliwość wejścia do gry od razu. defer jest spokojniejszy i przewidywalny, bo pozwala HTML-owi dokończyć pracę, a potem uruchamia kod w stabilnym momencie. Dla własnych komponentów front-endowych to zwykle bezpieczniejszy wybór. Następny krok to decyzja, kiedy który wariant naprawdę się opłaca.

Kiedy wybrać async, a kiedy defer

Ja zwykle traktuję async jako rozwiązanie dla kodu, który ma być samowystarczalny. Jeśli skrypt nie potrzebuje DOM-u, nie zależy od kolejności uruchomienia innych plików i może pojawić się w dowolnym momencie, to właśnie ten atrybut ma najwięcej sensu.

Wybierz async, gdy skrypt jest niezależny

  • Analityka i tagi pomiarowe.
  • Widgety zewnętrzne, które same zarządzają swoim stanem.
  • Skrypty reklamowe lub testowe, które nie powinny blokować głównej treści.
  • Elementy, których opóźnienie nie psuje działania interfejsu.

To dobre miejsce na async, bo zysk polega nie tylko na braku blokowania HTML, ale też na tym, że strona nie czeka na kod, który nie jest krytyczny dla użytkownika. Jednocześnie trzeba zaakceptować ryzyko: jeśli taki skrypt zależy od innego pliku, kolejność może się rozjechać.

Przeczytaj również: npm init w Node.js - Jak zacząć projekt bez błędów?

Wybierz defer, gdy kod zależy od DOM lub innych skryptów

  • Nawigacja, menu, zakładki i inne elementy interfejsu, które odwołują się do istniejących już węzłów DOM.
  • Własne bundle aplikacji, które inicjalizują komponenty po załadowaniu strony.
  • Skrypty, które muszą działać w określonej kolejności, na przykład biblioteka najpierw, a kod aplikacji później.
  • Logiczne inicjalizacje formularzy, walidacja i zachowania zależne od gotowej struktury dokumentu.

defer daje tu więcej kontroli, bo eliminuje przypadkowość kolejności. Jeśli kilka plików ma współpracować, wolę właśnie ten wariant albo moduły ES. Dzięki temu unikam klasycznego błędu: „czasem działa, czasem nie działa”, który zwykle wynika z losowego momentu wykonania skryptu. To prowadzi prosto do kolejnej kwestii, czyli najczęstszych potknięć.

Najczęstsze błędy, które psują efekt

Przy tych atrybutach problem rzadko leży w samych przeglądarkach. Zazwyczaj to kod jest źle dopasowany do sposobu ładowania. Poniżej są błędy, które widzę najczęściej, i które najłatwiej wyłapać już na etapie przeglądu kodu.

  • Używanie async dla skryptów zależnych od kolejności Jeśli jeden plik zakłada, że drugi już się wykonał, async potrafi to rozbić. To szczególnie częste przy starszych integracjach, gdzie biblioteka i wtyczka są ładowane osobno.
  • Zakładanie, że defer działa na inline Dla skryptów osadzonych bez src ten atrybut nie daje oczekiwanego efektu. W praktyce liczą się przede wszystkim zewnętrzne pliki.
  • Dodawanie obu atrybutów „na wszelki wypadek” To nie jest neutralna kompozycja. Jeśli oba są obecne, przeglądarka zachowuje się tak, jakby działał tylko async, więc nie dostajesz pożądanego kompromisu.
  • Mylenie DOMContentLoaded z pełnym załadowaniem strony To wydarzenie oznacza, że dokument został sparsowany i skrypty odroczone się wykonały. Nie oznacza to jeszcze końca ładowania obrazów, ramek czy wszystkich zasobów.
  • Trzymanie się starego nawyku „na koniec body i już” To czasem działa, ale nie rozwiązuje problemu kolejności ani nie daje takiej czytelności jak świadomy wybór między async i defer.

Najgroźniejszy błąd jest prosty: skrypt wygląda na mały i nieszkodliwy, ale ukrywa zależność od DOM-u albo od innego pliku. Wtedy losowy moment wykonania staje się problemem produkcyjnym. Z tego powodu warto jeszcze spojrzeć na nowoczesne moduły, bo one zmieniają reguły gry.

Jak działają moduły ES i dlaczego często nie potrzebują defer

Jeśli pracujesz w nowoczesnym kodzie, bardzo możliwe, że zamiast klasycznego używasz type="module". To ważne, bo moduły ES zachowują się jak skrypty odroczone domyślnie. Innymi słowy, w wielu przypadkach dodatkowy defer po prostu nic już nie wnosi.

  • type="module" oznacza, że skrypt jest pobierany równolegle i wykonywany po parsowaniu dokumentu.
  • defer na module nie daje dodatkowego efektu, bo moduły i tak są odroczone.
  • async na module zmienia zachowanie na bardziej agresywne, czyli skrypt uruchamia się natychmiast po pobraniu.
  • W przypadku modułów kolejność importów kontrolujesz przede wszystkim przez samą strukturę importów, a nie przez przypadkowe ustawienia w HTML.
  • Przy większych aplikacjach przydaje się też wcześniejsze preładowanie modułów, jeśli mają być potrzebne natychmiast po starcie.

To właśnie tutaj widać, że wybór nie sprowadza się do prostego „szybciej albo wolniej”. Chodzi o to, kiedy kod ma się uruchomić względem DOM i innych zależności. Jeśli projekt startuje dziś od modułów, ja traktuję je jako domyślny punkt wyjścia, a dopiero potem decyduję, czy któryś entry point naprawdę wymaga async. Na koniec zostaje najprostsza reguła wyboru, którą stosuję najczęściej.

Jedna reguła decyzyjna, która upraszcza wybór

  1. Jeśli skrypt dotyka DOM-u, korzysta z innych plików albo musi wykonać się w określonej kolejności, wybierz defer.
  2. Jeśli skrypt jest niezależny i może uruchomić się w dowolnym momencie, wybierz async.
  3. Jeśli używasz type="module", załóż zachowanie zbliżone do defer i dodawaj async tylko wtedy, gdy naprawdę akceptujesz mniej przewidywalny moment wykonania.

W praktyce różnica między tymi atrybutami nie polega na „magicznej optymalizacji”, tylko na kontroli nad kolejnością i momentem startu kodu. Jeśli mam wybrać bez długiego zastanawiania się, dla własnego front-endu zwykle stawiam na defer, a async zostawiam dla rzeczy naprawdę niezależnych. To daje stabilniejsze zachowanie, mniej losowości i mniej nocnych poprawek po tym, jak pozornie drobny skrypt przestaje działać po stronie użytkownika.

FAQ - Najczęstsze pytania

Async pobiera i wykonuje skrypt równolegle, bez gwarancji kolejności. Defer pobiera równolegle, ale wykonuje po parsowaniu HTML, zachowując kolejność. Async jest dla niezależnych skryptów, defer dla tych zależnych od DOM.

Async jest idealny dla skryptów niezależnych od DOM i innych plików, np. analityka, widgety zewnętrzne czy skrypty reklamowe. Uruchamiają się one natychmiast po pobraniu, nie blokując renderowania strony.

Defer stosuj dla skryptów zależnych od DOM lub innych skryptów, np. nawigacja, własne bundle aplikacji czy walidacja formularzy. Zapewnia wykonanie po sparsowaniu HTML i zachowuje kolejność, co daje stabilność.

Nie, type="module" domyślnie zachowuje się jak skrypt odroczony (defer). Dodatkowy atrybut defer na module zazwyczaj nie wnosi nic nowego. Async na module zmienia jego zachowanie na natychmiastowe wykonanie po pobraniu.

Najczęstszym błędem jest używanie async dla skryptów zależnych od kolejności lub DOM, co prowadzi do błędów. Inne to zakładanie, że defer działa na skryptach inline, lub dodawanie obu atrybutów jednocześnie (wtedy działa tylko async).

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi:

defer vs async async defer różnice kiedy async czy defer async defer do czego async defer type module

Udostępnij artykuł

Jacek Zając

Jacek Zając

Nazywam się Jacek Zając i od dziewięciu lat zajmuję się programowaniem webowym. Moja przygoda z tą dziedziną zaczęła się od fascynacji tworzeniem stron internetowych, co szybko przerodziło się w pasję do nauczania innych. Lubię dzielić się wiedzą i pomagać osobom, które stawiają pierwsze kroki w programowaniu. Skupiam się na wyjaśnianiu złożonych zagadnień w przystępny sposób, aby każdy mógł zrozumieć podstawy i rozwijać swoje umiejętności. W moich artykułach poruszam różnorodne tematy związane z programowaniem webowym, od HTML i CSS po JavaScript i frameworki. Dokładam wszelkich starań, aby informacje, które prezentuję, były rzetelne, aktualne i łatwe do przyswojenia. Regularnie śledzę nowinki w branży, co pozwala mi na dostarczanie czytelnikom treści zgodnych z najnowszymi trendami. Wierzę, że dobrze zorganizowana wiedza to klucz do sukcesu w karierze programisty.

Napisz komentarz