JavaScript ожидает внутри оператора if, задерживая выполнение кода за пределами блока if

Я всегда думал, что async/await — это просто синтаксический сахар вокруг Promises, и прежде чем его выполнить, движок JavaScript:

  1. Обертывает код после await в .then()
  2. Обертывает код внутри блока catch {} в .catch()
  3. Обертывает код внутри блока finally {} в .finally()

Но потом в статье MDN об await я прочитал это await

приостанавливает выполнение окружающих

Это принципиально другое. Я придумал пример тестирования этого.

const promise = new Promise((res, rej) => {
  setTimeout(() => {
    console.info('promise resolves');
    res();
  }, 2000);
});

async function asyncFunc() {
  if (Math.random() > 0.5) {
    await promise;
    console.info('#1 this executes after a delay 50% times');
  }
  else {
    console.info('#2 this executes immediately 50% times');
  }

  console.info('this should not wait for promise to resolve but it does');
}

asyncFunc();

Приведенный выше код подтверждает утверждение о том, что выполнение кода фактически приостанавливается, потому что, если мы попытаемся перевести его в «обещанную» версию, у нас будет другой код, а именно оператор console.info, находившийся за пределами оператора if/else, должен быть продублирован, чтобы реплицировать async/await поведение

const promise = new Promise((res, rej) => {
  setTimeout(() => {
    console.info('promise resolves');
    res();
  }, 2000);
});

function promiseFunc() {
  if (Math.random() > 0.5) {
    promise.then(() => {
      console.info('#1 this executes after a delay 50% times')
      console.info('this code, that was outside of if/else either goes here')
    })
  }
  else {
    console.info('#2 this executes immediately 50% times');
    console.info('or here, based on the result of if')
  }
}
promiseFunc();

Из-за этого у меня возникает ощущение, что Promise и async/await либо немного разные вещи, либо перевод кода крайне нелогичен.

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

const promise = new Promise((res, rej) => {
  setTimeout(() => {
    console.info('promise resolves');
    res();
  }, 2000);
});

function promiseFunc() {
  if (Math.random() > 0.5) {
    promise.then(() => {
      console.info('#1 this executes after a delay 50% times')
    })
  }
  else {
    console.info('#2 this executes immediately 50% times');
  }

  console.info('this is outside of if/else block and should be executed regardless of await keyword above');
}
promiseFunc();

Вопрос в том:

  1. Если он действительно делает паузу, можем ли мы сказать, что async/await и промисы — это не одно и то же?
  2. Если он на самом деле преобразуется в версию Promise перед выполнением, почему он игнорирует блоки кода и распространяется за их пределы?

await фактически помещает весь последующий код функции в .then(). Это выходит за рамки блока.

Pointy 09.07.2024 03:15

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

Patrick Roberts 09.07.2024 03:16

«Синтаксический сахар» — такой бессмысленный термин. Но если что-то async/await и является «сахаром» в функциях генератора, то не обещания. evertpot.com/syntactic-sugar . Но вы правы, они не одинаковы.

Evert 09.07.2024 03:25

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

Brad 09.07.2024 03:29

@Pointy, но если весь код после await поместить в then(), он также скопируется в else? Разве это не выглядит странно?

fires3as0n 09.07.2024 03:31

@fires3as0n в этом весь смысл await. На самом деле это не «пауза», это похоже на то, как если бы функция выполнялась как синхронная функция.

Pointy 09.07.2024 03:33

@fires3as0n также else не имеет значения, потому что ветка if уже занята. else может и не быть.

Pointy 09.07.2024 03:34

Тот факт, что async/await является синтаксическим сахаром для работы с промисами, не требует, чтобы где-либо происходило лексическое преобразование из async/await в .then. Это не одно и то же. Например, вы можете получить правильные трассировки стека с помощью await.

Paul 09.07.2024 03:35

@Пол, да, это важный момент. Удобно подумать о преобразовании await кода в .then(), но на самом деле это не совсем так. Это больше похоже на то, как работают функции-генераторы, но даже в этом случае лучше просто «довериться Силе» и кодировать с await так, как будто это семантически вводит «паузу».

Pointy 09.07.2024 03:37

@Pointy, если это не пауза, ее следует перевести в версию Promise перед выполнением, а рабочий перевод выглядит как другой код, вот что сбивает с толку. Также вы сначала говорите, что «на самом деле это не «пауза»», а затем сразу же «кодируете с ожиданием, как если бы оно семантически налагало «паузу». - так это пауза и все-таки нет?

fires3as0n 09.07.2024 03:39

@fires3as0n опять же, это больше похоже на работу генераторов. await похож на yield под одеялом. Так что это что-то вроде .then(), но не совсем. На самом деле, как я уже сказал, лучше всего верить, что это работает, и как только вы к этому привыкнете, вы сможете углубиться в детали.

Pointy 09.07.2024 03:43
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
11
72
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если он действительно делает паузу, можем ли мы сказать, что async/await и промисы — это не одно и то же?

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

Если он на самом деле преобразуется в версию Promise перед выполнением, почему он игнорирует блоки кода и распространяется за их пределы?

Он не игнорирует все блоки кода. Он их переводит.

Полная цитата из MDN на самом деле звучит так: «… приостанавливает выполнение окружающей функции async». Он не только приостанавливает выполнение блока if.

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

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

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

fires3as0n 09.07.2024 06:33

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

Bergi 09.07.2024 07:16

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

fires3as0n 09.07.2024 12:49

@fires3as0n Конечно, но вы не можете ожидать, что их внутренняя работа будет интуитивно понятной :-) Кроме того, не все среды одинаковы, и вы не спрашивали о конкретном инструменте. Babel делает это совсем иначе (с реальным преобразованием исходного кода), чем современный JS-движок, который действительно способен приостанавливать выполнение функции, а затем возобновлять его позже, очень похоже на то, как доходность работает в функциях-генераторах. Это гораздо эффективнее, чем выполнять преобразования исходного кода и создавать огромное количество сложных замыканий.

Bergi 09.07.2024 13:02

Другие вопросы по теме