Как использовать внешний инициализатор в цикле for и setTimeout?

#javascript #typescript

#javascript #typescript

Вопрос:

Пожалуйста, посмотрите на
https://stackblitz.com/edit/typescript-7hch6s

 function main(): void {
  let loopIndex: number = 2;
  let actions: string[] = ["dance", "walk", "run", "sit", "kick"];
  for (; loopIndex < 5; loopIndex  ) {
    doAction(actions[loopIndex]);
    window.setTimeout(() => doAction(actions[loopIndex]), 1000);
  }
}
function doAction(action: string) {
  console.log("action is "   action);
}
main();
/////////
The output in the console is
action is run
action is sit
action is kick
action is undefined
action is undefined
action is undefined
 

Я получаю три неопределенных параметра, в которых я хочу, чтобы значения run sit kick печатались в последних трех строках консоли.
Это проблема области видимости? Я попробовал метод bind. Но я не смог найти решение.

Ответ №1:

Проблема в том, что к тому времени, когда вызывается первое действие внутри setTimeout, значение loopIndex уже равно 5, а действия [5] неизвестны.

Самый простой способ решить эту проблему — переместить инициализацию переменной в цикл for . Это работает из-за правил определения области действия ключевых слов let:

 for (let loopIndex: number = 2; loopIndex < 5; loopIndex  ) {
    doAction(actions[loopIndex]);
    window.setTimeout(() => doAction(actions[loopIndex]), 1000);
}
 

или вы могли бы использовать закрытие:

 function main(): void {
  let loopIndex: number = 2;
  let actions: string[] = ["dance", "walk", "run", "sit", "kick"];
  for (; loopIndex < 5; loopIndex  ) {
    doAction(actions[loopIndex]);
    delayDoAction(actions[loopIndex]);
  }
}

function delayDoAction(action: string) {
  window.setTimeout(() => {
    doAction(action)
  }, 1000);
}

function doAction(action: string) {
  console.log("action is "   action);
}
main();
 

или найти какой-либо другой способ увеличения индекса внутри функции doAction.

Комментарии:

1. Спасибо, что предложили закрытие. Кажется, это работает. Мне придется некоторое время их изучать. Я не согласен с первым предложением в вашем сообщении.

2. @rickz чтобы продемонстрировать первый оператор, вы можете добавить console.log(startIndex) в обратный вызов setTimeout перед doAction, и вы увидите, что 5 печатается 3 раза.

3. Спасибо, что помогли мне. Я попробовал ваше последнее предложение. Я получаю сообщение об ошибке: startIndex не определен. Я буду продолжать изучать замыкания. Мне трудно их понять.

4. Чтобы прояснить мой вопрос, я переименовал startIndex в loopIndex . Я использовал ваше предложение в своем Stackblitz, и оно там работает. Я также изменил свой проект Angular, чтобы использовать замыкание. Теперь это работает. Я все еще изучаю замыкания. Я до сих пор не понимаю, почему это работает.

5. Вы правы. Ваше первое утверждение верно. Мне жаль, что я сомневался в вас. Но я бы изменил имя вашей функции с delayDoAction на capturedAction. Это описывало бы функциональность закрытия.