Przejdź do głównej zawartości

Bug #056

🪲 Znajdź buga

async function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}

async function main() {
const numbers = [1, 2, 3]
const store = []

numbers.forEach(async (num) => {
await sleep(1000)
store.push(num)
})

console.log(store)
}
main()

Chcemy zasymulować wykonanie asynchronicznych operacji na danych.

Funkcja sleep() zatrzymuje działanie kodu na określoną ilość milisekund symulując asynchroniczne operacje. Następnie przetworzone dane z pierwotnej tablicy numbers chcemy dodać do nowej store.

Czy powyższy kod zadziała prawidłowo?

Co zostanie zalogowane do konsoli?

🧪 Rozwiązanie

async function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}

async function main() {
const numbers = [1, 2, 3]
const store = []

const promises = numbers.map(async (num) => {
await sleep(1000)
store.push(num)
})
await Promise.all(promises)

console.log(store)
}
main()

W przykładzie z błędem do konsoli zostanie zalogowana pusta tablica []. Nasz kod nie zadziała więc zgodnie z planem.

Metoda forEach(), wywołuje funkcję przekazaną w argumencie jako callback w sposób synchroniczny.

Działa to więc tak samo, jak gdybyśmy w zwykłej funkcji wywołali inną, zagnieżdżoną, asynchroniczną.

Kod wewnątrz takiej funkcji byłby oddelegowany do wykonania na później w pętli zdarzeń (jako microtask).

Nieco lepszy obraz sytuacji da nam zamiana forEach() na map() i zalogowanie do konsoli takiej mapy.

Okaże się wtedy, że nowa, zmapowana tablica będzie zawierać nierozwiązane obiekty Promise.

W celu rozwiązania tablicy z obietnicami, możemy użyć Promise.all().

Aby wykonać to synchronicznie i nie używać Promise.all().then(), dodajemy await przed wywołaniem Promise.all() i czekamy, aż wszystkie obietnice z tablicy zostaną rozwiązane.

Następnie logując do konsoli store, mamy już dostęp do wszystkich elementów. Wszystkie obietnice zostały rozwiązane i tym samym wykonane zostały wszystkie wywołania push() na tablicy store.

Alternatywnym rozwiązaniem może być wywołanie pętli for, która w przeciwieństwie do forEach() nie przyjmuje funkcji callback, a tym samym użycie await wewnątrz iteracji pętli zostaje wykonane zgodnie z oczekiwaniami.

for (const num of numbers) {
await sleep(1000)
store.push(num)
}

🎢 Plac zabaw

Otwórz edytor w nowym oknie

📑 Linki

❤️ Podobają Ci się bugi JS?

Podziel się linkiem ze znajomymi:
https://codisity.pl/100-bugow-js