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.