Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Контекст функции при вызове по таймеру (https://javascript.ru/forum/misc/42884-kontekst-funkcii-pri-vyzove-po-tajjmeru.html)

Bombus 13.11.2013 07:27

Контекст функции при вызове по таймеру
 
Здравствуйте. При изучении учебника по Javascript возник вопрос. В теме про таймеры есть упражнение "Вывод чисел каждые 100мс". Код решения:
function printNumbersInterval20_100() {
  var i = 1;
  var timerId = setInterval(function() {
    console.log(i);
    if (i == 20) clearInterval(timerId);
    i++;
  }, 100);
}
 
// вызов
printNumbersInterval20_100();

Не понял расклад по контексту (видимости переменных) в случае вызова setInternal() из функции (не из глобального контекста), в нашем случае printNumbersInterval20_100. Из текста статьи, цитирую: "Вызов через setTimeout не передаёт контекст this. В частности, вызов метода объекта через setTimeout сработает в глобальном контексте". Так вот если наша анонимная функция сработает через указанное время в глобальном контексте, то откуда она будет знать про переменную i, которая определена локально во внешней функции? Как я понимаю, setInternal не блокирует выполнение скрипта, в данном случае не останавливает выполнение функции printNumbersInterval20_100. Т.е.создав таймер "в отдельном потоке", функция продолжает выполнятся и в конце концов заканчивается. Тогда откуда берется сохраненное значение i?

ksa 13.11.2013 08:34

Цитата:

Сообщение от Bombus
Тогда откуда берется сохраненное значение i?

Так это значение передано, как и значение timerId...

Bombus 13.11.2013 08:49

Разве передано? function(), т.е. параметры не передаются. А тело функции, имхо, будет получать значения исходя из контекста, исходя из окружения в момент выполнения, который случиться позже, когда внешняя функция printNumbersInterval20_100() уже завершиться. Да и контекст анонимной функции, согласно статье, глобальный. Где я ошибаюсь?

ksa 13.11.2013 08:55

Цитата:

Сообщение от Bombus
Разве передано?

Да.

Цитата:

Сообщение от Bombus
function(), т.е. параметры не передаются.

Для function() они глобальные...

Цитата:

Сообщение от Bombus
А тело функции, имхо, будет получать значения исходя из контекста, исходя из окружения в момент выполнения, который случиться позже

Дело уже сделано. И нет уже никакого "позже"...

ksa 13.11.2013 09:01

Bombus, почитай коментарии к этой статье до конца...

Bombus 13.11.2013 09:02

Ok, а как же (цитата из учебника):
"Вызов через setTimeout не передаёт контекст this. В частности, вызов метода объекта через setTimeout сработает в глобальном контексте".
Т.е. переменные из внешней функции передаются, а контекст нет?

ksa 13.11.2013 09:03

Цитата:

Сообщение от Bombus
контекст this

Не стоит равнять this с "обычними" переменными... :nono:

Bombus 13.11.2013 22:58

ksa, предложу немного измененный вариант исходного кода:
function printNumbersInterval20_100() {
  var i = 1, point = 'start';
  var timerId = setInterval(function() {
    console.log(i + ' ' + point);
    if (i == 20) clearInterval(timerId);
    i++;
  }, 100);
  point = 'finish'; 
}

В логах пишется:
1 finish
2 finish
.........
20 finish

Кажется механизм передачи переменных следующий:
1. При выполнении функции printNumbersInterval20_100() создается анонимная функция, которая привязывается к таймеру. Также ей передается ссылка на VariableEnviroment (а может и на LexicalEnvironment) от вызывающей функции, но все-же это не передача параметров функции.
2. printNumbersInterval20_100() уже может выполнится полностью, когда таймер сработает первый раз, но завершение функции будет означать лишь остановку действий внешней функции, а VariableEnviroment останется висеть в памяти. Примерно как это происходит с замыканиями - внешняя функция вроде завершена, но список переменных из памяти не выгружается пока на них будут ссылаться другие функции.
3. Анонимная функция при срабатывания таймера берет значения из переданного объекта VariableEnviroment. Т.е. ей не передается снимок переменных в момент создания анонимной функции из printNumbersInterval20_100(), а именно переменные в функцию попадают по ссылке в момент сработки таймера.
Еще один вывод: если таймеров создается много, то необходимо их после выполнения(!) чистить "ручками", а не оставлять на без присмотра, иначе будет копиться мусор и могут появиться тормоза. Где-то на форуме видел рекомендации про чистку при создании тысяч таймеров в обязательном порядке, иначе система может тормозить.
Если я не прав, прошу поправить.

BETEPAH 13.11.2013 23:27

Bombus,
У вас 6я строка отрабатывает каждые 0.1 секунды, а 8я еще ДО того момента, когда закончится первый интервал. Может так понятнее будет:
function printNumbersInterval20_100() {
  var i = 1, point = 'start';
  var timerId = setInterval(function() {
    console.log(i + ' ' + point);
    if (i == 20) clearInterval(timerId);
    i++;
	point = (point =='finish') ? 'start' : 'finish';
  }, 100);
}
printNumbersInterval20_100();

Bombus 14.11.2013 00:19

BETEPAH,
Что-то не понял, что вы хотели сказать. Т.е. подтверждаете тот механизм передачи значения переменных через VariablrEnviroment?
Мне показалось что в вашем коде пропущена строка
point = 'finish';
в конце функции printNumbersInterval20_100, т.к. в логах занчения point чередуются между 'start' и 'finish'.


Часовой пояс GMT +3, время: 12:35.