Ты наконец-то поймешь асинхронность в JS

Привет, друзья! Сегодня мы поговорим о том, что такое асинхронность в JavaScript и как она работает. Это одна из тех вещей, которые кажутся сложными, но на самом деле довольно просты, как только разберешься.

Понятие асинхронности и синхронности

Синхронный код — это код, который выполняется последовательно, один за другим. Ничего сложного, просто пошаговое выполнение. Для примера, вот как может выглядеть синхронный код:

console.log('Шаг 1');
console.log('Шаг 2');
console.log('Шаг 3');

А асинхронный код может выполняться параллельно с другими задачами. Это как многозадачность в нашей повседневной жизни: вы можете делать несколько вещей одновременно. Вот пример асинхронного кода:

console.log('Шаг 1');

setTimeout(function() {
    console.log('Шаг 2');
}, 2000);

console.log('Шаг 3');

Практический пример с setTimeout

В примере выше мы использовали setTimeout, чтобы задержать выполнение определенной функции на две секунды. Заметьте, что функция в setTimeout выполняется асинхронно, а остальной код продолжает выполняться без ожидания.


Конечно, введем более детальные примеры и разберем, какие методы являются микрозадачами, а какие макрозадачами.

Микрозадачи и макрозадачи

В JavaScript некоторые операции создают микрозадачи, а некоторые — макрозадачи. Давайте рассмотрим примеры.

Микрозадачи

Микрозадачи обычно связаны с операциями, связанными с промисами. Вот несколько примеров операций, создающих микрозадачи:

  1. Разрешение или отклонение промиса с помощью методов .then(), .catch() и .finally().

  2. Использование async/await в асинхронных функциях.

Давайте рассмотрим пример с использованием промисов:

console.log('Начало');

Promise.resolve()
  .then(() => console.log('Это микрозадача'))
  .then(() => console.log('Это еще одна микрозадача'));

console.log('Конец');

Этот код создает две микрозадачи с помощью методов .then(). Заметьте, что они выполняются после основной задачи, несмотря на то, что они добавляются в очередь раньше.

Макрозадачи

Макрозадачи обычно связаны с более крупными операциями или событиями в JavaScript, такими как выполнение скриптов, обработка событий DOM или выполнение кода в таймауте. Вот несколько примеров операций, создающих макрозадачи:

  1. Выполнение скрипта.

  2. Обработка событий DOM.

  3. Выполнение кода в таймауте с помощью setTimeout() или setInterval().

Рассмотрим пример с использованием setTimeout():

console.log('Начало');

setTimeout(() => {
  console.log('Это макрозадача');
}, 0);

console.log('Конец');

Этот код создает макрозадачу с помощью функции setTimeout(). Заметьте, что она добавляется в очередь после основной задачи и даже после микрозадач.

Что будет обработано первее?

В рамках итерации цикла сначала выполняется макрозадача, затем — очередь микрозадач. Пока есть задачи — цикл их выполняет, а когда их нет — ожидает новых задач.

Преимущества понимания микро и макрозадач

Понимание различия между микрозадачами и макрозадачами важно для эффективного управления асинхронным кодом в JavaScript. Это помогает предсказать порядок выполнения операций и избежать неожиданных результатов.

Как это работает: Event Loop

Как происходит выполнение этих задач? Здесь на сцену выходит Event Loop — он следит за стеком вызова и очередями задач, управляя порядком выполнения. Как ведущий дирижер, он гарантирует, что все работает как надо.

Преимущества асинхронности

Асинхронный код позволяет создавать более отзывчивые и эффективные веб-приложения, не замораживая интерфейс. Он позволяет выполнять задачи в фоновом режиме, не блокируя основной поток выполнения.

Асинхронные методы и их особенности

Теперь, когда мы понимаем основы асинхронного программирования, давайте рассмотрим некоторые ключевые асинхронные методы и их особенности.

Пример с использованием Promise

console.log('Начало');

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('Выполнено!');
    }, 2000);
});

promise.then((result) => {
    console.log(result);
});

console.log('Конец');

В этом примере мы создаем Promise, который выполняется через две секунды. Затем мы используем метод .then(), чтобы обработать результат выполнения Promise. Заметьте, что код после создания Promise не ждет его выполнения и продолжает работу.

Пример с использованием async/await

console.log('Начало');

async function myAsyncFunction() {
    const result = await new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('Выполнено!');
        }, 2000);
    });

    console.log(result);
}

myAsyncFunction();

console.log('Конец');

Здесь мы определяем асинхронную функцию, которая ожидает выполнения Promise с помощью ключевого слова await. Это позволяет нам писать код, который выглядит как синхронный, но на самом деле выполняется асинхронно.

Преимущества использования Promise и async/await

  • Читаемость кода: Promise и async/await делают асинхронный код более читаемым и понятным, особенно при выполнении сложных последовательностей операций.

  • Управление ошибками: Promise предоставляет удобный способ обработки ошибок с помощью метода .catch(), а async/await позволяет использовать блок try/catch для обработки ошибок.

  • Избегание callback hell: Использование цепочек Promise или async/await позволяет избежать так называемой «callback hell» — ситуации, когда множество вложенных обратных вызовов делают код трудным для чтения и отладки.

callback hell во своей своей красе

callback hell во своей своей красе

Заключение

Надеюсь, что этот более глубокий обзор асинхронного программирования в JavaScript помог вам лучше понять, как использовать асинхронные методы, а также какие преимущества они предоставляют.

ab804616bdbd6dc3af1bbc59dea31aeb.png

Поиграться с асинхронностью можно на этом сайте https://www.jsv9000.app/

© Habrahabr.ru