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
та записує в журнал.
Ви можете перевірити цю поведінку, увійшовши всередину об’єкта, що отримує / налаштовує властивості (у цьому прикладі 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;
}
await (x += 5)
іx += await 5
.