Наверное вы замечали что страницы сайтов в какой то момент перестают отвечать и "зависают". По большей части это происходит потому, что JS - однопоточный язык. А вся асинхронность это не часть самого языка, а часть браузерного WebAPI или среды NodeJS.

Так же для реализации мнопоточности с определенными ограничениями можно использовать Web Workers API. https://developer.mozilla.org/ru/docs/Web/API/Web_Workers_API/Using_web_workers

Но вернемся к основной теме, почему страницы зависают. Все дело в Event Loop - событийном цикле.

Это бесконечный цикл, который с определенным приоритетом обрабатывает задачи, полученные от разных заказчиков:

  1. Macrotasks - сам js скрипт, события, отложенные задачи - setTimeout, setInterval, ajax и т.д.

  2. Microtasks - promises, MutationObserver, queueMicrotask(task)

  3. Render - задачи на отрисовку

Как отрабатывает цикл

  1. Сначала event loop берет одну макротаску и выполняет ее.
  2. После ее выполнения, он переходит к очереди microtasks и выполняет ее пока она не закончится, если их нет - пропускает и идет дальше.
  3. Выполняет задачи на 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>