TL; DR: Поскольку +=
читает x
раньше, но записывает его после его изменения из-за ключевого слова await
во втором операнде (правая часть).
async
функции выполняются синхронно при вызове до первого оператора await
.
Таким образом, если вы удаляете await
, он ведет себя как обычная функция (за исключением что он все еще возвращает Обещание).
В этом случае вы получаете 5
и 6
в консоли:
let x = 0;
async function test() {
x += 5;
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
Первый await
останавливает синхронный запуск, даже если его аргумент доступен синхронно, поэтому следующее будет возвращать 1
и 6
, как вы ожидаете:
let x = 0;
async function test() {
// Enter asynchrony
await 0;
x += 5;
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
Однако ваш случай немного сложнее.
Вы поместили await
в выражение, которое использует +=
.
Вы, наверное, знаете, что в JS x += y
идентично x = (x + y)
. Я буду использовать последнюю форму для лучшего понимания:
let x = 0;
async function test() {
x = (x + await 5);
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
Когда переводчик достигает этой строки ...
x = (x + await 5);
... он начинает его оценивать и превращается в ...
x = (0 + await 5);
... затем он достигает await
и останавливается.
Код после вызова функции начинает выполняться, изменяет значение x
и регистрирует его.
x
теперь 1
.
Затем, после выхода из основного сценария, интерпретатор возвращается к приостановленной функции test
и продолжает вычислять эту строку:
x = (0 + 5);
И, поскольку значение x
уже подставлено, оно остается 0
.
Наконец, интерпретатор выполняет сложение, сохраняет от 5
до x
и регистрирует it.
Вы можете проверить это поведение, войдя внутрь объекта getter / setter свойства объекта (в этом примере y.z
отражает значение x
:
let x = 0;
const y = {
get z() {
console.log('get x :', x);
return x;
},
set z(value) {
console.log('set x =', value);
x = value;
}
};
async function test() {
console.log('inside async function');
y.z += await 5;
console.log('x :', x);
}
test();
console.log('main script');
y.z += 1;
console.log('x :', x);
/* Output:
inside async function
get x : 0 <-- async fn reads
main script
get x : 0
set x = 1
x : 1
set x = 5 <-- async fn writes
x : 5 <-- async fn logs
*/
/* Just to make console fill the available space */
.as-console-wrapper {
max-height: 100% !important;
}