Асинхронный JavaScript (обратные вызовы, обещания, асинхронный/ожидание)

В этом посте мы разберем и попытаемся понять асинхронный JavaScript и его общие шаблоны, Callback, Promises и Async/Await и их значение.

Синхронный JavaScript

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

Давайте рассмотрим пример синхронного кода

Как мы знаем, JavaScript интерпретирует код по одной строке за раз, поэтому приведенное выше будет выполняться таким образом.

  1. Сначала будет определена функция otherFunction().
  2. Это будет консольный журнал «Пуск» на консоли.
  3. Теперь, когда функция вызывается, и JavaScript добавляет функцию вызова в контекст выполнения и выполняет код внутри функции. Когда функция выполняется, она извлекается из контекста и переключается обратно в основной.
  4. И он выполняет остальную часть кода по одной строке за раз

Вывод приведенного выше кода выглядит следующим образом

Пример асинхронного кода

Асинхронный код или функция используются для выполнения указанной задачи, такой как выборка данных из API, к тому времени, когда остальная часть кода должна выполниться так, как она должна работать, не дожидаясь завершения асинхронного кода. Давайте рассмотрим простой пример кода, чтобы понять, как мы можем имитировать функцию setTimeout() для понимания асинхронного вызова.

Функция setTimeout() используется для задержки задачи на определенное время, указанное в качестве параметра (в миллисекундах).

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

В соответствии с нашим примером первая строка будет выполнена, и «Старт» будет отображаться в консоли, а затем, скорее, ожидая истечения времени ожидания, будет выполнена следующая строка, которая отобразит «Конец» в консоли, и когда асинхронный вызов будет завершено (через 2 секунды) на консоли отобразится строка «Мы находимся в тайм-ауте». Как также показано в выводе ниже,

Использование асинхронного вызова синхронным образом иногда приводит к некоторым проблемам, чтобы понять, что мы будем использовать небольшой пример кода для входа пользователя в систему, когда пользователь предоставляет адрес электронной почты и пароль, мы получаем его/ее имя пользователя с сервера/функции.

Теперь, когда мы вызываем функцию loginUser(), для ее завершения требуется 5 секунд, а до тех пор выполняется остальная часть кода, когда мы пытаемся получить сведения о пользователе, регистрируя его в консоли, он отображает undefined, поскольку данные все еще не обработаны.

Для решения такого рода проблем нам нужны функции обратного вызова.

Обратные вызовы

Обратный вызов – это функция, которая должна выполняться после завершения выполнения другой функции, отсюда и название "обратный вызов".

В JavaScript функции могут принимать функции в качестве аргументов и могут возвращаться другими функциями.

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

Теперь, когда мы передаем функцию обратного вызова в качестве параметра, мы можем использовать предоставление результата, обернув результат в callback({result}) вместо его возврата, и тем самым мы можем получить значение обратного вызова, которое может быть указано как функция стрелки в вызывающей функции, чтобы использовать результат.

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

Таким образом, используя обратный вызов, мы получим желаемый результат следующим образом:

Точно так же, если нам нужны некоторые другие детали, такие как сообщения пользователя, мы можем вызвать функцию getPosts() внутри обратного вызова, а затем получить ее значение.

Как мы видим, когда нам нужны данные о сообщениях, мы должны были использовать функцию getPosts() внутри результата обратного вызова, когда мы получаем пользователя, и получаем значение обратного вызова сообщений внутри него, теперь, поскольку мы хотим все больше и больше данных, которые у нас есть чтобы продолжать размещать обратный вызов внутри вложенной структуры, которая называется Ад обратного вызова.

Чтобы предотвратить это и сделать наш код намного чище, нам нужно назвать Promises.

Обещания

Объект Promise представляет возможное завершение (или сбой) асинхронной операции и ее результирующее значение (веб-документы MDN).

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

Promise находится в одном из следующих состояний:

  • ожидание: исходное состояние, ни выполнено, ни отклонено.
  • выполнено: означает, что операция выполнена успешно.
  • отклонено: означает, что операция не удалась.

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

Давайте просто посмотрим пример кода, описывающий, как промисы работают в асинхронном JavaScript.

Из приведенного выше фрагмента мы можем сказать, что promise — это объект, который предоставляет результат при его потреблении, это делается с использованием метода .then(), который выполняется, когда обещание разрешается или результат извлекается из API. , если существует, его можно обработать, связав функцию .catch(), которая будет выполнять указанную операцию, когда обещание будет отклонено.

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

Примечание. Вы не всегда будете возвращать промисы из своей функции, так как большую часть времени они будут возвращаться из API, который вы используете для получения данных, приведенный выше код — это просто имитация промиса. который будет съеден позже.

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

мы можем просто связать функцию .then(), чтобы получить данные, что сделает наш код намного чище, проще для понимания и отладки.

Что, если мы попытаемся написать код синхронно, который мы пишем обычным образом, как это

Чтобы сделать этот подход еще более простым, который может быть похож на синхронный способ написания кода, существует способ, называемый Async/Await, который является более синтаксическим сахаром, который по-прежнему использует Promises. на заднем фоне.

Асинхронно и ждать

Существует специальный синтаксис для более удобной работы с промисами.

Ключевое слово async перед функцией означает одну простую вещь: функция всегда возвращает обещание. Другие значения автоматически оборачиваются в разрешенное обещание. Используя асинхронность, мы можем выполнять задачу асинхронно.

Есть еще одно ключевое слово, await, которое работает только внутри async функций, ключевое слово await говорит JavaScript ждать, пока не будет получен результат обещания.

Синхронное использование обещаний с использованием async/await выглядит так, см. пример кода ниже.

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

Как мы можем выполнять обработку ошибок в async/await, как раньше, используя метод .catch(), путем цепочки. Таким образом, мы можем добиться обработки ошибок, просто поместив наш код в блок try / catch следующим образом:

Блок try содержит код, который должен быть выполнен, а блок catch содержит ошибку, возникающую во время выполнения.