Мы знаем, что функции Promise и Async/await используются для решения асинхронной проблемы в JavaScript, от начальной функции обратного вызова для обработки асинхронного, до Promise для обработки асинхронного, до Generator для обработки асинхронного, а затем до Async/await для обработки асинхронного. каждый раз Технологические обновления делают способ обработки асинхронной обработки в JavaScript более элегантным. С текущей точки зрения, Async/await считается окончательным решением для асинхронной обработки, что делает асинхронную обработку JS все более и более похожей на синхронные задачи. Наивысшее состояние асинхронного программирования — это вообще не заботиться о том, является ли оно асинхронным.

Эволюция асинхронных решений

1. Функция обратного вызова

Судя по раннему коду Javascript, до рождения ES6 практически вся асинхронная обработка была реализована на основе функций обратного вызова. Возможно, вы видели следующий код:

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

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

Поэтому, чтобы решить эту проблему, сообщество сначала предложило и реализовало Promise, а ES6 вписал его в стандарт языка, чтобы унифицировать использование.

2.Обещание

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

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

Таким образом, мы можем изменить приведенную выше функцию обратного вызова на эту: (при условии, что ajax был обернут Promise)

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

Но у промисов есть и свои недостатки:

  • Внутренняя ошибка Promise не может быть обнаружена с помощью try catch, она может быть обнаружена только вторым обратным вызовом или catch of then
  • Как только обещание будет создано, оно будет выполнено немедленно и не может быть отменено.

3.Генератор

Функции-генераторы — это решение для асинхронного программирования, предоставляемое ES6, и их синтаксическое поведение полностью отличается от традиционных функций. Функции-генераторы выводят асинхронное программирование JavaScript на совершенно новый уровень.

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

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

После вызова функции Generator функция не выполняется, а возвращаемая функция является не результатом работы функции, а объектом-указателем, указывающим на внутреннее состояние, то есть объект Iterator. Далее, следующий метод объекта traverser необходимо вызвать, чтобы переместить указатель в следующее состояние.

Таким образом, приведенную выше функцию обратного вызова можно записать так:

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

Логика работы следующего метода объекта-обходчика следующая.

(1) При обнаружении выражения yield выполнение следующих операций приостанавливается, а значение выражения, следующего сразу за выражением yield, используется в качестве значения атрибута value возвращаемого объекта.

(2) В следующий раз, когда вызывается следующий метод, продолжайте выполнение до тех пор, пока не встретится следующее выражение yield.

(3) Если новое выражение yield не встречается, оно будет выполняться до конца функции до оператора return и использовать значение выражения, следующего за оператором return, в качестве значения атрибута value возвращаемого объекта.

(4) Если функция не имеет оператора return, атрибут value возвращаемого объекта не определен.

Само выражение yield не возвращает значение или всегда возвращает значение undefined. Следующий метод может принимать один параметр, который будет использоваться в качестве возвращаемого значения предыдущего выражения yield.

Как понять эту фразу? Давайте посмотрим на следующий пример:

Поскольку yield не имеет возвращаемого значения, значение после выполнения (yield(x+1)) не определено, поэтому второе выполнение a.next() фактически равно 2*undefined, поэтому значение равно NaN, поэтому следующее b In пример , 12 передается, когда b.next() выполняется во второй раз, и это будет рассматриваться как возвращаемое значение первого выполнения b.next(), поэтому его можно правильно рассчитать в примере б. Здесь значение value в результате выполнения next нельзя путать с возвращаемым значением yield, это не одно и то же.

Разница между доходностью и доходностью

Тот же пункт:

  • возвращает значение выражения, следующего за оператором
  • Может приостановить выполнение функции

разница:

  • Функция может иметь несколько выходов, но только один возврат
  • yield имеет функцию запоминания позиции, return - нет.

4.Асинхронно/ожидание

Async/await на самом деле является синтаксическим сахаром вышеупомянутого генератора, асинхронная функция фактически эквивалентна функции funciton *, а ожидание эквивалентно функции yield. В механизме асинхронного/ожидания функция автоматического выполнения порождения, которую мы инкапсулировали выше включается автоматически.

Таким образом, приведенную выше функцию обратного вызова можно записать более кратко:

Улучшение асинхронной функции до функции генератора отражено в следующих четырех пунктах:

  • Встроенный исполнитель: выполнение асинхронной функции такое же, как и у обычной функции, в отличие от функции-генератора, которая должна вызывать следующий метод или использовать модуль co для фактического выполнения.
  • Более ясная семантика: async и await имеют более четкую семантику, чем звездочки и yield. async означает, что в функции есть асинхронная операция, а await означает, что следующему выражению необходимо дождаться результата.
  • Более широкая применимость: в соответствии с соглашением модуля co, за командой yield может следовать только функция Thunk или объект Promise, в то время как за командой await в асинхронной функции может следовать объект Promise и значения примитивного типа (числовые, строковые и Логические значения, но на этот раз они будут автоматически преобразованы в немедленно разрешенный объект Promise).
  • Возвращаемое значение — это Promise: возвращаемое значение асинхронной функции — это объект Promise, что намного удобнее, чем возвращаемое значение функции Generator, являющейся объектом Iterator. Вы можете указать следующее действие с помощью метода then.

асинхронная функция

Возвращаемое значение асинхронной функции — это объект Promise, поэтому он может вызывать метод then.

выражение ожидания

Выражение в правой части await обычно является объектом обещания, но также может быть и другим значением.

  • Если выражение является объектом обещания, await возвращает значение обещания, которое было выполнено успешно.
  • Если выражение является любым другим значением, используйте это значение напрямую как возвращаемое значение ожидания.
  • Объект Promise за ожиданием блокирует следующий код, объект Promise разрешается, а затем в результате выражения ожидания получается значение разрешения.
  • Вот почему ожидание должно использоваться в асинхронном режиме, асинхронный возвращает объект Promise, который может выполнять блокировку асинхронно.

Сравнение асинхронных схем

Последние три решения предлагаются для решения традиционной функции обратного вызова, поэтому их преимущества перед функцией обратного вызова очевидны. А async/await — это синтаксический сахар для функции Генератора.

  • Внутренняя ошибка Promise не может быть обнаружена с помощью try catch, она может быть обнаружена только вторым обратным вызовом или функцией catch, а ошибка async/await может быть обнаружена с помощью try catch.
  • Как только обещание будет создано, оно будет выполнено немедленно и не будет блокировать следующий код, в то время как объект обещания, стоящий за ожиданием в асинхронной функции, будет блокировать следующий код.
  • Асинхронная функция неявно возвращает обещание, а значение разрешения обещания — это значение, возвращаемое функцией.
  • Использование асинхронной функции может сделать код более кратким. Ему не нужно вызывать метод then для получения возвращаемого значения, такого как промис, не нужно писать анонимную функцию для обработки значения разрешения промиса и не нужно определять избыточные переменные данных. Это также позволяет избежать вложенного кода.