Вам надо было обернуть setTimeout в Promise:
const summarize = (num1, num2) => num1 + num2;
const divide = (num1, num2) => num1 / num2;
let x = 2;
let y = 6;
let z = 10;
const calcAsync = async() => {
x = summarize(x, x);
console.log(1, x);
x = await new Promise((resolve, reject) => {
setTimeout(() => resolve(summarize(x, y)));
});
console.log(2, x);
x = divide(x, z);
console.log(3, x);
}
calcAsync();
Объяснение того, почему нельзя обойтись без Promise:
P.S. Формально это утверждение - не верное. Как показал @Grundy достаточно заменить Promise на самый обычный thenable объект (объект у которого есть метод then) и в этом случае всё будет работать точно так же как и с Promise. Но этот ответ даст вам понимание того, почему не поможет await-тить или оборачивать в async ф-ию setTimeout
Рассмотрим этот кусок кода:
const calcAsync = async () => {
x = summarize(x, x);
x = await setTimeout(() => summarize(x, y), 1);
x = divide(x, z);
}
Т.к. async/await возвращает и работает Promise, то давайте переведём этот кусок кода на язык Promise-ов:
const calcAsync = () => {
return Promise.resolve()
.then(function () {
x = summarize(x, x);
return setTimeout(() => summarize(x, y), 1);
})
.then(function (handleResolved) {
x = handleResolved;
x = divide(x, z);
});
};
И так нас интересует строка с setTimeout. В документации сказано, что then на вход получит от Promise-а 2 аргумента - это handleResolved и handleRejected и так же сказано, что он сам тоже вернёт Promise. Нас интересует, только handleResolved, потому дальше handleRejected будет опущен. И так второй then на вход получит возращаемое значение setTimeout - это число timeoutID и абсолютно не важно что какая ф-ия будет запущена и когда она будет запущена. Следовательно во втором then, где строчка x = handleResolved в x будет записано число timeoutID и дальнейшие вычисления будут проводиться именно с этим значением. Что важно это число каждый раз будет разным, и перезапуская один и тот же код в одной сессии вы будете получать разные значения
Демо код (специально убрал x = await..., чтобы не подумали, что всё из-за того что в x что-то записывается):
const summarize = (num1, num2) => num1 + num2;
const divide = (num1, num2) => num1 / num2;
let x = 2;
let y = 6;
let z = 10;
const calcAsync = async() => {
x = summarize(x, x);
console.log(await setTimeout(() => summarize(x, y)));
console.log(await setTimeout(() => summarize(x, y)));
console.log(await setTimeout(() => summarize(x, y)));
console.log(await setTimeout(() => summarize(x, y)));
x = divide(x, z);
}
calcAsync();
Нам же надо чтобы в x во втором then попало значение summarize(x, y).
Автором был предложен вариант, обернуть setTimeout в async ф-ию т.к. async нам возвращает Promise. Замечу, что писать в обёртке что-то типа return await setTimeout будет так же бесполезно как и если без обёртки. Давайте посмотрим, что будет в этом случае:
const asyncSum = async () => {return setTimeout(() => summarize(x, y), 1)}
const calcAsync = async() => {
x = summarize(x, x);
x = await asyncSum();
x = divide(x, z);
}
переписвая это на язык Promise-ов получим:
const asyncSum = () => {
return Promise.resolve().then(function () {
return setTimeout(() => summarize(x, y), 1);
});
};
const calcAsync = () => {
return Promise.resolve()
.then(function () {
x = summarize(x, x);
return asyncSum();
})
.then(function (handleResolved) {
x = handleResolved;
x = divide(x, z);
});
};
Теперь можем спокойно подставить результат нашей ф-ии asyncSum туда, где он вызывается и получаем:
const calcAsync = () => {
return Promise.resolve()
.then(function () {
x = summarize(x, x);
return Promise.resolve().then(function () {
return setTimeout(() => summarize(x, y), 1);
});
})
.then(function (handleResolved) {
x = handleResolved;
x = divide(x, z);
});
};
И что мы видим? Мы опять отправляем в handleResolved наш timeoutID. Потому обёртки тут тоже не помогут отправить само значение summarize(x, y)
Теперь, когда разобрались, почему мы не можем ни await-тить ни оборачивать в async ф-ии наш setTimeout, посмотрим как нам помогает обёртка Promise-ом:
const calcAsync = async() => {
x = summarize(x, x);
x = await new Promise((resolve) => {
setTimeout(() => resolve(summarize(x, y)), 1);
});
x = divide(x, z);
}
опять переписываем на язык Promise-ов и получаем:
const calcAsync = () => {
return Promise.resolve()
.then(function () {
x = summarize(x, x);
return new Promise((resolve) => {
setTimeout(() => resolve(summarize(x, y)), 1);
});
})
.then(function (handleResolved) {
x = handleResolved;
x = divide(x, z);
});
};
Теперь видно, что мы явно в handleResolved передаём значение ф-ии summarize(x, y) в строке resolve(summarize(x, y)). Тут уже дело в том, что пока не будет вызыван/отработан resolve последующий then будет ждать, сколько бы это не заняло времени
P.S В документации сказано, что этот кусок кода:
async function foo() {
return 1
}
похоже на:
function foo() {
return Promise.resolve(1)
}
но я переписывал это как:
function foo() {
return Promise.resolve().then(function () {
return 1;
});
}
в самих рассуждениях и примерах это ни на что не влияет. Вся логика будет точно такой же, если переписывать как показано в документации, но чтобы не переключаться постоянно между методами resolve и then, решил что, если всегда будет then, то будет меньше путаницы и это позволит лучше понять что происходит. Надеюсь смог добиться этого :)