Как остановить выполнение функции js
Перейти к содержимому

Как остановить выполнение функции js

  • автор:

Как остановить функцию во время ее выполнения — JavaScript

Как я могу остановить/окончить функцию, которая уже выполнена и все еще работает? Например, у меня есть эта функция:

function foo() < setInterval(function() < console.log("Foo Executed !"); >, 3000); > foo();

Теперь эта функция foo() будет работать неограниченное время, когда происходит определенное событие, допустим, была нажата кнопка остановки, тогда я хочу остановить эту функцию.

В моем случае у функции нет функции setInterval() . А что если у функции foo() нет метода setInterval() , а просто много строк кода, которые выполняются, и я хочу остановить ее выполнение после определенного события.

Поделиться Источник 10 августа 2018 в 19:53

2 ответа

Остановка запущенной функции немного отличается от того, что вы фактически показываете для кода, что является асинхронной операцией, происходящей вне функции, которая ее инициировала.

Запущенные функции могут быть прерваны только изнутри функции, и это делается либо с помощью оператора return , либо с помощью выброса исключения.

return может быть вызван условно, чтобы функция не всегда выходила в одну и ту же точку. Часто это происходит с функциями проверки формы — если что-то определяется как недействительное, возникает return , чтобы форма не была отправлена. Если все является действительным, return пропускается и форма отправляется.

Вот простой пример с return :

function foo1() < console.log("Foo started. "); if(prompt("Type 1 to terminate right now or anything else to continue. ") == "1")< return; // Function will terminate here if this is encountered >console.log("Foo ending. "); // This will only be run if something other than 1 was entered > foo1();

И, вот пример с ошибкой (не то, что обычно делается):

function foo() < console.log("foo started. "); for(var i = 0; i < 5; i++)< if(i === 3) < throw "I HATE 3!"; >console.log(i); > console.log("foo ended. "); > foo();

Но с таймерами и интервалами вам нужно будет вызвать clearInterval() и/или clearTimeout() , чтобы остановить их. Это разные вещи, потому что, хотя какая-то функция может инициировать таймер или интервал, таймер работает вне среды выполнения JavaScript как WebAPI. Для этого мы должны отправить сообщение в WebAPI, что хотим, чтобы таймер перестал подсчитывать.

Теперь эта функция foo() будет работать неограниченное время, когда происходит определенное событие, допустим, была нажата кнопка остановки, затем я хочу остановить эту функцию.

Но foo не работает неограниченное время. Она запускается один раз, а затем заканчивается. Затем приблизительно через 3 секунды таймер вызывает анонимную функцию, которую вы передали ей, чтобы ее запустить, а затем эта функция заканчивается, и приблизительно через 3 секунды анонимная функция снова запускается, а затем снова заканчивается и так далее. Функция не работает последовательно, это интервальный таймер (WebAPI, который вызывает функцию для вызова).

А что если функция foo() не имеет метода setInterval() , а просто много строк кода, которые выполняются, и я хочу остановить его выполнение после определенного события.

Ваш вопрос, кажется, подразумевает, что вы хотите остановить функцию, которая выполняется в данный момент, когда происходит другое событие. Это не может произойти в JavaScript, так как JavaScript является однопоточной средой. Любое событие может быть вызвано и обработано только после того, как весь остальной код будет закончен. Таким образом, никогда не может быть сценария, подобного тому, о котором вы упоминаете, если мы не говорим об асинхронном коде. Асинхронный код — это код, который выполняется вне времени выполнения JavaScript. С помощью такого кода вы можете отправить сообщение в WebAPI, который обрабатывает этот внешний код, который вы хотите отменить/абортировать эту обработку, и это то, что мы делаем, когда вызываем clearInterval() .

document.getElementById("start").addEventListener("click", startInterval); document.getElementById("stop").addEventListener("click", stopInterval); // You'll need a variable to store a reference to the timer var timer = null; function startInterval() < // Then you initilize the variable timer = setInterval(function() < console.log("Foo Executed!"); >, 1500); > function stopInterval() < // To cancel an interval, pass the timer to clearInterval() clearInterval(timer); >
 

Как остановить выполнение функции js

Чтобы остановить выполнение функции, достаточно просто вызвать инстркуцию return . Например:

const func = (num) =>  // если переданное число равно 2, то останавливаем работу функции if (num === 2)  return; > console.log('hello'); >; func(1); // => hello func(2); 

В примере выше, если вызвана функция с параметром 2, то срабатывает условие и функция прерывается благодаря return . Также можно вернуть результат из функции:

const func = (num) =>  // если переданное число равно 2, то останавливаем работу функции if (num === 2)  return 'is number 2'; > return 'hello'; >; console.log(func(1)); // => hello console.log(func(2)); // => is number 2 

break

Оператор break прерывает выполнение текущего цикла, оператора множественного выбора switch или блочного выражения с меткой. Выполнение кода продолжается с конструкции, следующей за прерванной.

Интерактивный пример

Синтаксис

break [label]; 

label Необязательный

Идентификатор связанной метки. Если прерываемое выражение не цикл или switch , указание метки обязательно.

Описание

С помощью оператора break можно прервать выполнение блочного выражения с меткой. В таком случае break должен находиться внутри блока кода с указанной меткой. Само блочное выражение может быть любым, а не только циклом.

Использование break , с меткой или без, невозможно внутри функции, которая вложена в прерываемые цикл, конструкцию switch или блочное выражение с меткой.

Примеры

break в цикле while

Следующая функция использует оператор break для выхода из цикла while , когда i станет равно 3, и возвращает значение 3 * x .

function testBreak(x)  var i = 0; while (i  6)  if (i == 3)  break; > i += 1; > return i * x; > 

break в конструкции switch

В примере оператор break прервёт выполнение switch сразу после выполнения кода в совпавшем case .

const food = "sushi"; switch (food)  case "sushi": console.log("Sushi is originally from Japan."); break; case "pizza": console.log("Pizza is originally from Italy."); break; default: console.log("I have never heard of that dish."); break; > 

break в блочном выражении с указанной меткой

В примере используется оператор break с меткой. В таком случае break обязательно должен быть вложен в блок кода с той же меткой. Обратите внимание, что inner_block вложен в outer_block .

outer_block:  inner_block:  console.log("1"); break outer_block; // break прервёт выполнение кода как в inner_block, так и в outer_block console.log(":-("); // не будет выполнено > console.log("2"); // не будет выполнено > 

break вне блочного выражения с указанной меткой

В примере также используется break с меткой, но код будет выполнен с ошибкой SyntaxError , потому что break находится в блоке с меткой block_1 , а прерывается выполнение блока кода с меткой block_2 . Ещё раз, в таком случае break обязательно должен быть вложен в блок кода с меткой, выполнение которого требуется прервать.

block_1:  console.log('1'); break block_2; // SyntaxError: label not found > block_2:  console.log('2'); > 

break внутри функций

Код в примерах также будет выполнен с ошибкой SyntaxError , потому что в обоих случаях break находится в теле функции, вложенной в прерываемый цикл или прерываемое блочное выражение с меткой.

function testBreak(x)  var i = 0; while (i  6)  if (i == 3)  (function()  break; >)(); > i += 1; > return i * x; > testBreak(1); // SyntaxError: Illegal break statement 
block_1:  console.log('1'); ( function()  break block_1; // SyntaxError: Undefined label 'block_1' >)(); > 

Спецификации

Specification
ECMAScript Language Specification
# sec-break-statement

Совместимость с браузерами

BCD tables only load in the browser

Планирование: setTimeout и setInterval

Мы можем вызвать функцию не в данный момент, а позже, через заданный интервал времени. Это называется «планирование вызова».

Для этого существуют два метода:

  • setTimeout позволяет вызвать функцию один раз через определённый интервал времени.
  • setInterval позволяет вызывать функцию регулярно, повторяя вызов через определённый интервал времени.

Эти методы не являются частью спецификации JavaScript. Но большинство сред выполнения JS-кода имеют внутренний планировщик и предоставляют доступ к этим методам. В частности, они поддерживаются во всех браузерах и Node.js.

setTimeout

let timerId = setTimeout(func|code, [delay], [arg1], [arg2], . );

func|code Функция или строка кода для выполнения. Обычно это функция. По историческим причинам можно передать и строку кода, но это не рекомендуется. delay Задержка перед запуском в миллисекундах (1000 мс = 1 с). Значение по умолчанию – 0. arg1 , arg2 … Аргументы, передаваемые в функцию

Например, данный код вызывает sayHi() спустя одну секунду:

function sayHi() < alert('Привет'); >setTimeout(sayHi, 1000);
function sayHi(phrase, who) < alert( phrase + ', ' + who ); >setTimeout(sayHi, 1000, "Привет", "Джон"); // Привет, Джон

Если первый аргумент является строкой, то JavaScript создаст из неё функцию.

Это также будет работать:

setTimeout("alert('Привет')", 1000);

Но использование строк не рекомендуется. Вместо этого используйте функции. Например, так:

setTimeout(() => alert('Привет'), 1000);

Передавайте функцию, но не запускайте её

Начинающие разработчики иногда ошибаются, добавляя скобки () после функции:

// неправильно! setTimeout(sayHi(), 1000);

Это не работает, потому что setTimeout ожидает ссылку на функцию. Здесь sayHi() запускает выполнение функции, и результат выполнения отправляется в setTimeout . В нашем случае результатом выполнения sayHi() является undefined (так как функция ничего не возвращает), поэтому ничего не планируется.

Отмена через clearTimeout

Вызов setTimeout возвращает «идентификатор таймера» timerId , который можно использовать для отмены дальнейшего выполнения.

Синтаксис для отмены:

let timerId = setTimeout(. ); clearTimeout(timerId);

В коде ниже планируем вызов функции и затем отменяем его (просто передумали). В результате ничего не происходит:

let timerId = setTimeout(() => alert("ничего не происходит"), 1000); alert(timerId); // идентификатор таймера clearTimeout(timerId); alert(timerId); // тот же идентификатор (не принимает значение null после отмены)

Как мы видим из вывода alert , в браузере идентификатором таймера является число. В других средах это может быть что-то ещё. Например, Node.js возвращает объект таймера с дополнительными методами.

Повторюсь, что нет единой спецификации на эти методы, поэтому такое поведение является нормальным.

Для браузеров таймеры описаны в разделе таймеров стандарта HTML5.

setInterval

Метод setInterval имеет такой же синтаксис как setTimeout :

let timerId = setInterval(func|code, [delay], [arg1], [arg2], . );

Все аргументы имеют такое же значение. Но отличие этого метода от setTimeout в том, что функция запускается не один раз, а периодически через указанный интервал времени.

Чтобы остановить дальнейшее выполнение функции, необходимо вызвать clearInterval(timerId) .

Следующий пример выводит сообщение каждые 2 секунды. Через 5 секунд вывод прекращается:

// повторить с интервалом 2 секунды let timerId = setInterval(() => alert('tick'), 2000); // остановить вывод через 5 секунд setTimeout(() => < clearInterval(timerId); alert('stop'); >, 5000);

Во время показа alert время тоже идёт

В большинстве браузеров, включая Chrome и Firefox, внутренний счётчик продолжает тикать во время показа alert/confirm/prompt .

Так что если вы запустите код выше и подождёте с закрытием alert несколько секунд, то следующий alert будет показан сразу, как только вы закроете предыдущий. Интервал времени между сообщениями alert будет короче, чем 2 секунды.

Вложенный setTimeout

Есть два способа запускать что-то регулярно.

Один из них setInterval . Другим является вложенный setTimeout . Например:

/** вместо: let timerId = setInterval(() => alert('tick'), 2000); */ let timerId = setTimeout(function tick() < alert('tick'); timerId = setTimeout(tick, 2000); // (*) >, 2000);

Метод setTimeout выше планирует следующий вызов прямо после окончания текущего (*) .

Вложенный setTimeout – более гибкий метод, чем setInterval . С его помощью последующий вызов может быть задан по-разному в зависимости от результатов предыдущего.

Например, необходимо написать сервис, который отправляет запрос для получения данных на сервер каждые 5 секунд, но если сервер перегружен, то необходимо увеличить интервал запросов до 10, 20, 40 секунд… Вот псевдокод:

let delay = 5000; let timerId = setTimeout(function request() < . отправить запрос. if (ошибка запроса из-за перегрузки сервера) < // увеличить интервал для следующего запроса delay *= 2; >timerId = setTimeout(request, delay); >, delay);

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

Вложенный setTimeout позволяет задать задержку между выполнениями более точно, чем setInterval .

Сравним два фрагмента кода. Первый использует setInterval :

let i = 1; setInterval(function() < func(i); >, 100);

Второй использует вложенный setTimeout :

let i = 1; setTimeout(function run() < func(i); setTimeout(run, 100); >, 100);

Для setInterval внутренний планировщик будет выполнять func(i) каждые 100 мс:

Реальная задержка между вызовами func с помощью setInterval меньше, чем указано в коде!

Это нормально, потому что время, затраченное на выполнение func , использует часть заданного интервала времени.

Вполне возможно, что выполнение func будет дольше, чем мы ожидали, и займёт более 100 мс.

В данном случае движок ждёт окончания выполнения func и затем проверяет планировщик и, если время истекло, немедленно запускает его снова.

В крайнем случае, если функция всегда выполняется дольше, чем задержка delay , то вызовы будут выполняться без задержек вообще.

Ниже представлено изображение, показывающее процесс работы рекурсивного setTimeout :

Вложенный setTimeout гарантирует фиксированную задержку (здесь 100 мс).

Это потому, что новый вызов планируется в конце предыдущего.

Сборка мусора и колбэк setTimeout/setInterval

Когда функция передаётся в setInterval/setTimeout , на неё создаётся внутренняя ссылка и сохраняется в планировщике. Это предотвращает попадание функции в сборщик мусора, даже если на неё нет других ссылок.

// функция остаётся в памяти до тех пор, пока планировщик обращается к ней setTimeout(function() , 100);

Для setInterval функция остаётся в памяти до тех пор, пока не будет вызван clearInterval .

Есть и побочный эффект. Функция ссылается на внешнее лексическое окружение, поэтому пока она существует, внешние переменные существуют тоже. Они могут занимать больше памяти, чем сама функция. Поэтому, если регулярный вызов функции больше не нужен, то лучше отменить его, даже если функция очень маленькая.

setTimeout с нулевой задержкой

Особый вариант использования: setTimeout(func, 0) или просто setTimeout(func) .

Это планирует вызов func настолько быстро, насколько это возможно. Но планировщик будет вызывать функцию только после завершения выполнения текущего кода.

Так вызов функции будет запланирован сразу после выполнения текущего кода.

Например, этот код выводит «Привет» и затем сразу «Мир»:

setTimeout(() => alert("Мир")); alert("Привет");

Первая строка помещает вызов в «календарь» через 0 мс. Но планировщик проверит «календарь» только после того, как текущий код завершится. Поэтому «Привет» выводится первым, а «Мир» – после него.

Есть и более продвинутые случаи использования нулевой задержки в браузерах, которые мы рассмотрим в главе Событийный цикл: микрозадачи и макрозадачи.

Минимальная задержка вложенных таймеров в браузере

В браузере есть ограничение на то, как часто внутренние счётчики могут выполняться. В стандарте HTML5 говорится: «после пяти вложенных таймеров интервал должен составлять не менее четырёх миллисекунд.».

Продемонстрируем в примере ниже, что это означает. Вызов setTimeout повторно вызывает себя через 0 мс. Каждый вызов запоминает реальное время от предыдущего вызова в массиве times . Какова реальная задержка? Посмотрим:

let start = Date.now(); let times = []; setTimeout(function run() < times.push(Date.now() - start); // запоминаем задержку от предыдущего вызова if (start + 100 < Date.now()) alert(times); // показываем задержку через 100 мс else setTimeout(run); // если нужно ещё запланировать >); // пример вывода: // 1,1,1,1,9,15,20,24,30,35,40,45,50,55,59,64,70,75,80,85,90,95,100

Первый таймер запускается сразу (как и указано в спецификации), а затем задержка вступает в игру, и мы видим 9, 15, 20, 24. .

Аналогичное происходит при использовании setInterval вместо setTimeout : setInterval(f) запускает f несколько раз с нулевой задержкой, а затем с задержкой 4+ мс.

Это ограничение существует давно, многие скрипты полагаются на него, поэтому оно сохраняется по историческим причинам.

Этого ограничения нет в серверном JavaScript. Там есть и другие способы планирования асинхронных задач. Например, setImmediate для Node.js. Так что это ограничение относится только к браузерам.

Итого

  • Методы setInterval(func, delay, . args) и setTimeout(func, delay, . args) позволяют выполнять func регулярно или только один раз после задержки delay , заданной в мс.
  • Для отмены выполнения необходимо вызвать clearInterval/clearTimeout со значением, которое возвращают методы setInterval/setTimeout .
  • Вложенный вызов setTimeout является более гибкой альтернативой setInterval . Также он позволяет более точно задать интервал между выполнениями.
  • Планирование с нулевой задержкой setTimeout(func,0) или, что то же самое, setTimeout(func) используется для вызовов, которые должны быть исполнены как можно скорее, после завершения исполнения текущего кода.
  • Браузер ограничивает 4-мя мс минимальную задержку между пятью и более вложенными вызовами setTimeout , а также для setInterval , начиная с 5-го вызова.

Обратим внимание, что все методы планирования не гарантируют точную задержку.

Например, таймер в браузере может замедляться по многим причинам:

  • Перегружен процессор.
  • Вкладка браузера в фоновом режиме.
  • Работа ноутбука от аккумулятора.

Всё это может увеличивать минимальный интервал срабатывания таймера (и минимальную задержку) до 300 или даже 1000 мс в зависимости от браузера и настроек производительности ОС.

Задачи

Вывод каждую секунду

важность: 5

Напишите функцию printNumbers(from, to) , которая выводит число каждую секунду, начиная от from и заканчивая to .

Сделайте два варианта решения.

  1. Используя setInterval .
  2. Используя рекурсивный setTimeout .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *