let x = 0;
async function test() {
x += await 5;
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
Значения x
вошли в систему 1
и 5
. Мой вопрос: почему значение x
5
второго журнала?
Если test
после выполняется x += 1
(так как это асинхронная функция), то значение x равно 1 к моменту времени test
, поэтому x += await 5
должно быть значение x
6
.
javascript
async-await
event-loop
АЛЬДРИН П ВИНСЕНТ
источник
источник
await (x += 5)
иx += await 5
.Ответы:
TL; DR: потому что
+=
читаетx
раньше, но записывает после изменения, из-заawait
ключевого слова во втором операнде (правая часть).async
функции выполняются синхронно при вызове до первогоawait
оператора.Таким образом, если вы удалите
await
его, он будет вести себя как обычная функция (за исключением того, что он по-прежнему возвращает Promise).В таком случае вы получаете
5
и6
в консоли:Первый
await
останавливает синхронный запуск, даже если его аргумент доступен синхронно, поэтому возвращается следующее1
и6
, как вы ожидаете:Тем не менее, ваш случай немного сложнее.
Вы поместили
await
в выражение, которое использует+=
.Вы, наверное, знаете, что в JS
x += y
идентичноx = (x + y)
. Я буду использовать последнюю форму для лучшего понимания:Когда переводчик достигает этой строки ...
... он начинает оценивать это и превращается в ...
... затем он достигает
await
и останавливается.Код после вызова функции начинает выполняться и изменяет значение
x
, затем регистрирует его.x
в настоящее время1
.Затем, после выхода из основного сценария, интерпретатор возвращается к приостановленной
test
функции и продолжает вычислять эту строку:И, поскольку значение
x
уже подставлено, оно остается0
.Наконец, интерпретатор делает добавление, магазины ,
5
чтобыx
и регистрирует его.Вы можете проверить это поведение, зарегистрировавшись в свойствах объекта getter / setter (в этом примере
y.z
отражает значениеx
:источник
x += y
идентичноx = (x + y)
». - Это не так в каждой ситуации на каждом языке, но в целом вы можете рассчитывать на то, что они действуют одинаково.Ваше заявление не
x += await 5
соответствуетЗначение
_temp
orary0
, и если вы изменяетеx
во времяawait
(что делает ваш код), это не имеет значения, оно назначается5
впоследствии.источник
Этот код довольно сложен для отслеживания, потому что он выполняет несколько неожиданных асинхронных переходов назад и вперед. Давайте рассмотрим (близко), как это будет на самом деле, и я объясню почему потом. Я также изменил журналы консоли, чтобы добавить число - облегчает обращение к ним, а также показывает, что записывается:
Таким образом, код на самом деле не работает прямолинейно, это точно. И у нас тоже есть странная
4/7
вещь. И это действительно вся проблема здесь.Прежде всего, давайте уточним - асинхронные функции на самом деле не являются строго асинхронными. Они только приостановят выполнение и возобновят позже, если
await
будет использовано ключевое слово. Без этого они выполняются сверху вниз, выражение за выражением синхронно:Итак, первое, что нам нужно знать, что использование
await
заставит остальную часть функции выполняться позже. В данном примере это означает, чтоconsole.log('x1 :', x)
будет выполняться после остальной части синхронного кода. Это потому, что любые Обещания будут разрешены после завершения текущего цикла событий.Итак, это объясняет, почему мы
x2 : 1
регистрируемся первым и почемуx2 : 5
регистрируется вторым, но не почему последнее значение5
. По логикеx += await 5
должно быть5
... но вот второй улов кawait
ключевому слову - оно приостановит выполнение функции, но ничего, прежде чем она уже запустится.x += await 5
на самом деле будет обрабатываться следующим образомx
. На момент казни это так0
.await
следующее выражение, которое есть5
. Итак, функция приостанавливается сейчас и будет возобновлена позже.0 + 5
x
Итак, функция делает паузу после того, как прочитала, что
x
есть,0
и возобновляет, когда она уже изменена, однако она не перечитывает значениеx
.Если мы развернем
await
вPromise
эквивалент, который будет выполняться, у вас есть:источник
Да, это немного сложно, что на самом деле происходит, обе операции сложения происходят на пареллале, поэтому операция будет выглядеть так:
В пределах обещания:
x += await 5
==>x = x + await 5
==>x = 0 + await 5
==>5
Снаружи:
x += 1
==>x = x + 1
==>x = 0 + 1
==>1
Поскольку все вышеперечисленные операции выполняются слева направо, первая часть сложения может быть рассчитана одновременно, и, поскольку до 5 ожидается ожидание, эта добавка может немного задержаться. Вы можете увидеть выполнение, поместив точку останова в коде.
источник
Async и Await являются продолжением обещаний. Асинхронная функция может содержать выражение await, которое приостанавливает выполнение асинхронной функции и ожидает разрешения переданной Promise, а затем возобновляет выполнение асинхронной функции и возвращает разрешенное значение. Помните, ключевое слово await допустимо только внутри асинхронных функций.
Даже если вы изменили значение x после вызова тестовой функции, значение x останется равным 0, поскольку асинхронная функция уже создала свой новый экземпляр. То есть все переменные, находящиеся вне ее, не изменят значение внутри нее после ее вызова. Если только вы не поместите свой шаг выше тестовой функции.
источник
let x="Didn't receive change"; (async()=>{await 'Nothing'; console.log(x); await new Promise(resolve=>setTimeout(resolve,2000)); console.log(x)})(); x='Received synchronous change'; setTimeout(()=>{x='Received change'},1000)
он выводитReceived synchronous change
иReceived change