Наверное вы замечали что страницы сайтов в какой то момент перестают отвечать и "зависают". По большей части это происходит потому, что JS - однопоточный язык. А вся асинхронность это не часть самого языка, а часть браузерного WebAPI или среды NodeJS.
Так же для реализации мнопоточности с определенными ограничениями можно использовать Web Workers API. https://developer.mozilla.org/ru/docs/Web/API/Web_Workers_API/Using_web_workers
Но вернемся к основной теме, почему страницы зависают. Все дело в Event Loop - событийном цикле.
Это бесконечный цикл, который с определенным приоритетом обрабатывает задачи, полученные от разных заказчиков:
Macrotasks - сам js скрипт, события, отложенные задачи - setTimeout, setInterval, ajax и т.д.
Microtasks - promises, MutationObserver, queueMicrotask(task)
Render - задачи на отрисовку
Как отрабатывает цикл
Дальше все повторяется.
То есть, пока исполняются таска и микротаски, браузер не приступает к отрисовке.
Это означает, если код в таске или микротаске будет исполнятся 15 секунд, то все это время страница будет висеть и не реагировать.
Разберем простой пример
<script>
//Start Task
function sleep(ms) {
const start = new Date().getTime(), expire = start + ms;
while (new Date().getTime() < expire) {
}
}
function print(text) {
console.log(text);
}
function requestToServer(product) {
sleep(1);
return new Promise((resolve) => {
setTimeout(() => {
resolve({product, quantity: 1});
}, 15)
})
}
function bookProduct() {
sleep(1);
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, 10)
})
}
function checkStock(product) {
sleep(1);
return requestToServer(product);
}
function addToBasket(product) {
sleep(1)
checkStock(product).then((quantity) => {
//Start Microtask
sleep(1)
print(quantity)
bookProduct().then(() => {
//Start Microtask
sleep(1)
print('booked');
});
});
}
addToBasket('table');
//End Task
</script>