Дан код на JavaScript с асинхронными вызовами: async function f(){ for(var i=0;iconsole.log(i),100); } } f(); Объясните результат, предложите несколько корректных реализаций, и обсудите влияние лексической области видимости

7 Мая в 20:51
22 +1
0
Ответы
1
Объяснение результата
- В вашем коде переменная объявлена как `var i`. `var` — функциональная (не блочная) область видимости, поэтому внутри функции `f` существует одна общая переменная `i`, которая инкрементируется циклом до значения 333. setTimeout создаёт отложенные колбэки, которые выполнятся после завершения синхронного цикла. К тому моменту `i === 333`, и все три колбэка прочитают это же значение. В итоге в консоль попадут три строки с 3,3,33, 3, 33,3,3.
Краткий вывод: вывод будет 3,3,33, 3, 33,3,3.
Несколько корректных реализаций
1) Использовать `let` в заголовке цикла (ES6) — для каждой итерации создаётся своя связка переменной:
```js
async function f(){
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
}
f();
// Вывод: 0, 1, 2
```
2) Захват значения через IIFE (замыкание, старый стиль):
```js
async function f(){
for (var i = 0; i < 3; i++) {
(function(j){
setTimeout(() => console.log(j), 100);
})(i);
}
}
f();
// Вывод: 0, 1, 2
```
3) Передать аргумент в setTimeout (в отдельных средах/браузерах поддерживается):
```js
async function f(){
for (var i = 0; i < 3; i++) {
setTimeout((x) => console.log(x), 100, i);
}
}
f();
// Вывод: 0, 1, 2
```
4) Использовать функцию-обёртку при вызове:
```js
async function f(){
for (var i = 0; i < 3; i++) {
const j = i;
setTimeout(() => console.log(j), 100);
}
}
f();
// Вывод: 0, 1, 2
```
5) Если нужно последовательное ожидание между итерациями (меняет семантику):
```js
async function f(){
for (let i = 0; i < 3; i++) {
await new Promise(r => setTimeout(r, 100));
console.log(i);
}
}
f();
// Вывод (с паузами): 0, 1, 2
```
Влияние лексической области видимости (block vs function scope)
- `var` имеет функциональную область видимости: одна переменная `i` для всего цикла; замыкания внутри цикла будут ссылаться на одну и ту же переменную (поэтому всё выводят текущее/окончательное значение).
- `let` и `const` имеют блочную область видимости и в конструкциях `for` спецификация ES6 создаёт новую привязку переменной для каждой итерации. Замыкание, созданное на этой итерации, захватывает именно эту привязку, поэтому получает текущее значение итерации.
- Итого: при работе с асинхронными колбэками в цикле обычно предпочтительнее `let` или явный захват значения (IIFE/копия) — это даёт ожидаемое поведение.
Дополнительно: `async` в объявлении функции сам по себе тут ни на что не влияет, если вы не используете `await` внутри — цикл остаётся синхронным, колбэки выполнятся позже.
7 Мая в 21:03
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир