Аннотация: в этом посте мы начнем разговор о синхронном и асинхронном программировании и рассмотрим несколько небольших примеров, в которых асинхронное программирование лучше всего. Затем мы переходим к истории реализации асинхронного программирования, включая ад обратных вызовов и обещания (без async / await и с ними). Наконец, мы опишем и реализуем практическое использование асинхронной задачи javascript, которая применима к реальным приложениям.

Предварительное условие: в этой статье предполагается, что вы хорошо понимаете, как работает ES6 (в основном, стрелочные функции), как выполняется синхронный JavaScript (т. е. контексты выполнения, стек выполнения, область видимости переменных и т. д.) и как работают объекты в javascript.

Давайте начнем наш разговор с понятия синхронного JavaScript. Как правило, программный код выполняется по одной строке за другой, поэтому, например, если функция полагается на результат другой функции, она должна дождаться завершения и возврата другой функции, и пока это не произойдет, вся программа в основном приостанавливается из системы координат пользователя. НО, с точки зрения UX / UI, мы не хотим приостанавливать программу каждый раз, когда нам нужно выполнить функцию, которая занимает много времени (например, более 1000 мс), и именно в этом прелесть асинхронного программирования. вступает в игру.

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

Давайте лучше поймем эту концепцию, выбрав конкретную реализацию асинхронного javascript, которая представляет собой встроенный метод setTimeout function. Это приостанавливает нашу программу на заранее установленное время перед ее выполнением и, следовательно, позволяет запускать несколько вещей одновременно. Давайте полностью поймем это, рассмотрев следующий пример.

Пример: создайте простую программу javascript, которая принимает 2 функции, одна вложена внутри другой, во внешней функции она печатает «Я первый», затем запускает внутреннюю функцию и печатает «Готово», а для внутренней функции он просто печатает «Я второй».

Для простоты сначала рассмотрим синхронный случай, javascript анализирует 2 функции и сохраняет их в глобальном контексте выполнения. Когда вызывается primaryExp, он создает стек выполнения и выполняет следующие действия: «Я первый» - ›« Я второй »-› «Готово», и это можно увидеть, выполнив приведенную ниже суть.

В асинхронном случае javascript анализирует 2 функции и сохраняет их в глобальном контексте выполнения (как и в синхронном случае). НО, когда мы вызываем primaryExp, он выполнит «Я первый» - ›« Готово »-› (через 2 секунды (2000 мс)) - ›« Асинхронный блок ».

За кулисами асинхронного JavaScript:

  1. Во-первых, когда мы вызываем primaryExp, мы создаем новый контекст выполнения, затем создается контекст выполнения журнала, который выполняет наше сообщение («Я первый») и выскакивает из стека, оставляя в стеке только контекст выполнения primaryExp (). .
  2. Затем мы создаем второй контекст выполнения, называемый secondaryExp, который вызывает метод setTimeout из веб-API, что вызывает создание другого контекста выполнения. Это создает таймер и функцию обратного вызова в среде WEB API и присоединяется к методу secondaryExp.
  3. Метод setTimeout извлекается из стека и присоединяется к объекту таймера, а secondaryExp также извлекается из контекста выполнения, что приводит только к контексту выполнения primaryExp и глобальному контексту выполнения.
  4. Теперь мы выполняем последний оператор журнала в функции primaryExp и возвращаем «Finished». По истечении 2 секунд функция обратного вызова перемещается в очередь сообщений, ожидая срабатывания определенного события.
  5. Затем цикл событий подталкивает функцию обратного вызова из очереди сообщений в стек выполнения, что создает новый контекст выполнения.

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

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

Решение 1 (традиционный подход) [ES5]. Традиционный подход к этой проблеме заключается в выполнении нескольких обратных вызовов, использующих метод setTimeout, и каждый раз, когда вы запрашиваете что-то новое, вы просто выполняете еще один вызов сервер. Это можно продемонстрировать в сущности ниже.

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

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

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

Решение 2.0 (обещания без async / await) [ES6]:

Одним из предложений по решению этой проблемы было введение новой функции (в ES6), которая называлась Promises. Итак, в ES6 для решения этой проблемы были введены обещания, и, говоря простым языком, обещание - это объект, который отслеживает, произошло ли уже определенное событие или нет. Если событие действительно произошло, оно определяет, что происходит, и реализует концепцию будущей ценности, которую мы ожидаем.

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

Чтобы применить эти знания в javascript, давайте повторим наш предыдущий пример создания поддельной программы просмотра рецептов, но вместо традиционного подхода мы будем использовать обещания.

Объяснение Cod e: способ определения обещаний в javascript включает вызов new Promise (), который мы устанавливаем для getID. Внутри объекта Promise мы определяем 2 параметра, resolve и reject, которые имеют дело с состоянием обещания, resolve означает, что обещание было выполнено, а reject означает, что обещание Было отказано. Чтобы ответить на наш предыдущий примерный вопрос, нам нужно определить 3 обещания, которые выполняются после выполнения предыдущего обещания.

Эти 3 обещания определены как следующие функции: getID (отвечает за получение идентификаторов), getPatient (отвечает за получение информации о пациенте) и getPatientBasedOnDate (отвечает за получение информации о пациентах на основе указанной даты входа). Мы выполняем их, объединяя их в цепочку, используя два важных метода: then и catch. Метод then используется для выполнения обещания в случае успеха, а метод catch используется для обработки ошибок, таких как сервер не содержит определенного поля.

Решение 2.1 (обещания с использованием async / await) [ES8]:

В ES2017 (ES8) они выпустили новую функцию (на самом деле 2 мощных метода), чтобы помочь потоку обещаний. Эти функции помогают нам, разработчикам, использовать обещания (т. Е. Заменять методы then / catch методами async / await), и давайте разберемся, как это работает, используя async / await с нашими предыдущими 3 обещаниями. следующее:

ВАЖНО. Метод ожидания ТОЛЬКО работает в рамках асинхронной функции, и это работает только в браузерах, поддерживающих ES8 и выше.

Пример из реального мира: создайте программу javascript, которая извлекает все статьи от указанного среднего пользователя. Первое, что следует отметить, это то, что medium.com предоставляет RSS-канал (Rich Site Summary), который позволяет нам собирать различные фрагменты общедоступной информации о пользователе в формате XML. Этот RSS-канал доступен по URL-адресам в форме: https://medium.com/feed/@username. Во-вторых, существует сторонняя библиотека javascript под названием rss-parser (https://www.npmjs.com/package/rss-parser), которая преобразует поле XML в читаемые данные JSON.

ПРИМЕЧАНИЕ. Прежде чем пытаться выполнить код, прочтите приведенные выше комментарии к сути ниже. Для работы приведенного ниже кода требуются дополнительные элементы npm.

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

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