1.1 Podstawowe pojęcia asynchroniczności
Asynchroniczność w JavaScript pozwala na wykonywanie zadań w tle, nie blokując głównego wątku wykonania. To jest szczególnie ważne dla zadań, które mogą zająć znaczący czas, takich jak zapytania sieciowe, czytanie plików i timery. Teraz przyjrzymy się głównym koncepcjom programowania asynchronicznego w JavaScript i podamy przykłady użycia.
Jednowątkowy charakter JavaScript
JavaScript jest językiem jednowątkowym: oznacza to, że wykonuje kod sekwencyjnie w jednym wątku. Jednakże operacje asynchroniczne pozwalają na wykonywanie zadań w tle, uwalniając główny wątek dla innych zadań.
Event Loop (Pętla zdarzeń)
Pętla zdarzeń (Event Loop) jest kluczowym mechanizmem, który pozwala JavaScript przetwarzać zadania asynchroniczne. Pętla zdarzeń zarządza kolejką wiadomości (Message Queue) i kolejką mikrozadań (Microtask Queue), zapewniając wykonywanie operacji asynchronicznych.
- Kolejka wiadomości: zawiera zadania, takie jak obsługa zdarzeń, zapytania sieciowe i timery. Zadania z tej kolejki są wykonywane sekwencyjnie.
- Kolejka mikrozadań: zawiera zadania o wyższym priorytecie w porównaniu do zadań w kolejce wiadomości. Przykłady obejmują zakończenie promes i wywołania funkcji zwrotnych (callbacks) w mikrozadaniach.
Pętla zdarzeń stale sprawdza obie kolejki i wykonuje zadania z nich, gdy główny wątek staje się wolny.
Operacje asynchroniczne
Operacje asynchroniczne pozwalają na wykonywanie zadań w tle. Główne przykłady operacji asynchronicznych obejmują:
- Timery (
setTimeout,setInterval) - Obsługa zdarzeń
- Zapytania sieciowe (np.
XMLHttpRequest,Fetch API) - Czytanie/zapis plików (w Node.js)
Przyjrzyjmy się kilku przykładom operacji asynchronicznych.
1.2 Timery
Timery pozwalają na wykonywanie zadań z opóźnieniem lub w regularnych odstępach czasu.
Przykład użycia setTimeout
W tym przykładzie setTimeout ustawia wykonanie funkcji po 2 sekundach. W wyniku tego najpierw wyświetlane są Start i End, a następnie po 2 sekundach wyświetlane jest Executed after 2 seconds.
console.log('Start');
setTimeout(() => {
console.log('Executed after 2 seconds');
}, 2000);
console.log('End');
Przykład użycia setInterval
W tym przykładzie setInterval wykonuje funkcję co sekundę, zwiększając licznik i wyświetlając jego wartość. Gdy licznik osiągnie 5, interwał jest czyszczony za pomocą clearInterval:
let counter = 0;
const intervalID = setInterval(() => {
counter++;
console.log(`Counter: ${counter}`);
if (counter >= 5) {
clearInterval(intervalID);
}
}, 1000);
1.3 Obsługa zdarzeń
Obsługa zdarzeń pozwala na wykonywanie kodu w odpowiedzi na działania użytkownika lub inne zdarzenia.
Przykład użycia obsługi zdarzeń
W tym przykładzie obsługa zdarzenia click jest dodawana do przycisku. Gdy użytkownik naciska przycisk, wyświetlane jest wiadomość Button clicked!:
<!DOCTYPE html>
<html>
<head>
<title>Event Handler Example</title>
</head>
<body>
<button id="myButton">Click me</button>
<script>
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('Button clicked!');
});
</script>
</body>
</html>
1.4 Zapytania sieciowe
Zapytania sieciowe pozwalają na wykonywanie asynchronicznych zapytań HTTP do serwera.
Przykład użycia XMLHttpRequest
W tym przykładzie tworzony jest asynchroniczny request GET do API, a po zakończeniu zapytania odpowiedź jest wyświetlana w konsoli:
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
console.log(response);
}
};
xhr.send();
1.5 Event Loop w akcji
Aby lepiej zrozumieć, jak działa Event Loop, przyjrzyjmy się następującemu przykładowi:
console.log('Start');
setTimeout(() => {
console.log('Timeout 1');
}, 0);
Promise.resolve().then(() => {
console.log('Promise 1');
});
setTimeout(() => {
console.log('Timeout 2');
}, 0);
Promise.resolve().then(() => {
console.log('Promise 2');
});
console.log('End');
Oczekiwane wyjście będzie następujące:
StartEndPromise 1Promise 2Timeout 1Timeout 2
W tym przykładzie najpierw wykonywane są operacje synchroniczne (console.log('Start') i console.log('End')). Następnie wykonywane są mikrozadania (obsługa promes), i dopiero po tym wykonywane są zadania z kolejki wiadomości (setTimeout).
GO TO FULL VERSION