Когда переводчик сталкивается с await
, он приостанавливает функцию до разрешения Обещания. Даже если обещание выполнится немедленно, функция возобновит работу только в течение следующей микрозадачи. Напротив, массив итерируется через немедленно , синхронно. Когда вы делаете
const promises = arr.map(async () => {
count += await somePromise();
})
После того, как массив итерирован, но до разрешения await
s, "текущее" значение count
, которое принимается для использования +=
извлекается до , await
разрешается - и значение count
до этого равно 0. Таким образом, интерпретатор выглядит так, как будто есть 5 отдельных операторов:
count += await somePromise();
count += await somePromise();
count += await somePromise();
count += await somePromise();
count += await somePromise();
, которые разрешают что-то вроде
const currentValueOfCount = count;
count = currentValueOfCount + await somePromise();
count = currentValueOfCount + await somePromise();
count = currentValueOfCount + await somePromise();
count = currentValueOfCount + await somePromise();
count = currentValueOfCount + await somePromise();
Так, каждый раз, правая часть =
разрешается в 0 + 1
, поэтому в конце l oop , count
равно только 1.
Если вам интересно, где это описано в спецификации, посмотрите семантику для операторов присваивания . Где +=
является одним из AssignmentOperator
с, следующий синтаксис:
LeftHandSideExpression AssignmentOperator AssignmentExpression
делает:
- Пусть lref будет результатом вычисления LeftHandSideExpression.
- Пусть lval будет? GetValue (lref).
- Пусть rref будет результатом вычисления AssignmentExpression.
- Пусть rval будет? GetValue (rref).
- Пусть op будет @, где AssignmentOperator равен @ =.
- Пусть r будет результатом применения op к lval и rval, как если бы вычислялось выражение lval op rval.
Посмотрите, как lval
извлекается немедленно , прежде чем вычисляется правая часть оператора. (Если бы lval
было извлечено после оценки правой стороны, AssignmentExpression
, результаты были бы 5, как вы ожидаете)
Вот пример такого поведения без асинхронных операций:
let num = 5;
const fn = () => {
num += 3;
return 0;
}
num += 2 + fn();
console.log(num);
Выше num += 2 + fn();
немедленно извлекает num
как 5
для использования в +=
, затем вызывает fn()
. Хотя num
переназначается внутри fn
, это не имеет никакого эффекта, потому что значение num
уже было получено внешним +=
.
с вашим рабочим кодом , когда вы делаете
const nb = await somePromise();
count += nb;
Это поместит значение разрешения somePromise
в переменную nb
, и , тогда count += nb;
запустится. Это ведет себя как ожидалось, потому что «текущее» значение count
, используемое для +=
, извлекается после Обещание разрешается, поэтому, если предыдущая итерация переназначена count
, оно будет успешно учтено к следующей итерации.