Пошук уроків, статей та іншого контенту
Дізнайтеся, чому цикл може ніколи не завершитися та як такі помилки впливають на роботу програми або браузера. Навчіться знаходити проблеми в умовах завершення та безпечно проєктувати цикли.
Нескінченний цикл — це цикл, умова якого ніколи не стає false. У результаті програма продовжує виконувати ті самі інструкції без зупинки.
Наприклад:
while (true) {
console.log("Цей код виконується постійно");
}Умова true завжди істинна, тому цикл не має природного способу завершитися.
Нескінченні цикли можуть:
зайняти процесор;
споживати багато пам’яті;
заблокувати виконання іншого коду;
«зависити» програму або вкладку браузера;
ускладнити пошук помилки.
У браузері нескінченний синхронний цикл може повністю заблокувати сторінку. Кнопки перестануть реагувати, а інтерфейс не зможе оновлюватися.
Зазвичай цикл має три важливі частини:
Початкове значення.
Умову продовження.
Зміну значення, від якого залежить умова.
Розглянемо цикл while:
let count = 1;
while (count <= 3) {
console.log(count);
count += 1;
}Послідовність виконання:
count дорівнює 1.
Перевіряється умова count <= 3.
Виводиться значення count.
count збільшується на 1.
Умова перевіряється знову.
Коли count стане 4, умова буде false, і цикл завершиться.
Результат:
1
2
3Зверніть увагу на рядок:
count += 1;Саме він змінює значення, від якого залежить умова циклу.
У цьому прикладі count завжди залишається рівним 1:
let count = 1;
while (count <= 3) {
console.log(count);
// Помилка: count не збільшується
}Умова count <= 3 завжди істинна, тому цикл не завершиться.
Виправлений варіант:
let count = 1;
while (count <= 3) {
console.log(count);
count += 1;
}Цикл має завершитися після досягнення 5, але змінна зменшується:
let number = 1;
while (number <= 5) {
console.log(number);
number -= 1;
}Значення number буде таким:
1
0
-1
-2
-3
...Воно ніколи не стане більшим за 5.
Якщо ми хочемо рухатися від 1 до 5, потрібно збільшувати значення:
let number = 1;
while (number <= 5) {
console.log(number);
number += 1;
}Іноді помилка безпосередньо міститься в умові:
let isActive = true;
while (isActive) {
console.log("Програма працює");
}Значення isActive не змінюється на false, тому цикл триває без кінця.
Виправлення:
let isActive = true;
let attempts = 0;
while (isActive) {
console.log("Програма працює");
attempts += 1;
if (attempts === 3) {
isActive = false;
}
}У цьому прикладі цикл завершиться після трьох повторень.
Під час роботи з умовами легко переплутати присвоєння та порівняння.
= — присвоїти значення;
=== — перевірити строгу рівність.
Наприклад:
let status = "ready";
while (status === "ready") {
console.log("Готово");
// Якщо status не змінити, цикл буде нескінченним
}Потрібно змінити стан або використати іншу умову:
let status = "ready";
while (status === "ready") {
console.log("Готово");
status = "finished";
}forЦикл for зазвичай містить початкове значення, умову та крок зміни:
for (let index = 0; index < 3; index += 1) {
console.log(index);
}Тут:
let index = 0 — початкове значення;
index < 3 — умова продовження;
index += 1 — збільшення лічильника після кожної ітерації.
Нескінченний цикл може виникнути, якщо пропустити зміну лічильника або змінювати його неправильно:
for (let index = 0; index < 3; ) {
console.log(index);
// Помилка: index не змінюється
}Умова index < 3 залишається істинною постійно.
Безпечний варіант:
for (let index = 0; index < 3; index += 1) {
console.log(index);
}Технічно можна створити нескінченний for навмисно:
for (;;) {
// Цикл без умови та без автоматичного кроку
}Такий запис варто використовувати лише тоді, коли всередині є чіткий спосіб завершення через break.
breakОператор break негайно завершує найближчий цикл.
let number = 1;
while (true) {
console.log(number);
if (number === 3) {
break;
}
number += 1;
}Цей цикл формально має умову true, але завершується, коли number дорівнює 3.
break може бути корисним, коли завершення залежить від події, яку складно записати в загальну умову циклу.
Наприклад, пошук першого парного числа:
const numbers = [3, 7, 9, 12, 15];
let firstEvenNumber = null;
for (const number of numbers) {
if (number % 2 === 0) {
firstEvenNumber = number;
break;
}
}
console.log(firstEvenNumber);Результат:
12Однак не варто без потреби будувати всі цикли як while (true). Часто зрозуміліше одразу описати умову завершення.
continueОператор continue пропускає решту поточної ітерації та переходить до наступної.
for (let number = 1; number <= 5; number += 1) {
if (number === 3) {
continue;
}
console.log(number);
}Результат:
1
2
4
5У циклах із continue важливо не пропустити зміну лічильника. У циклі while це може призвести до нескінченного виконання:
let number = 1;
while (number <= 3) {
if (number === 2) {
continue;
}
number += 1;
}Коли number стане 2, виконається continue. Перехід до наступної ітерації відбудеться без збільшення number, тому значення 2 залишатиметься назавжди.
Безпечний варіант:
let number = 1;
while (number <= 3) {
if (number === 2) {
number += 1;
continue;
}
console.log(number);
number += 1;
}У цьому прикладі лічильник змінюється в кожній ітерації.
Перед написанням циклу корисно відповісти на кілька запитань:
Яке початкове значення має змінна?
Яка умова продовження циклу?
Яка змінна впливає на цю умову?
Де саме ця змінна змінюється?
У якому випадку цикл гарантовано завершиться?
Що станеться, якщо очікувана подія не відбудеться?
Якщо цикл залежить від зовнішніх даних або події, додайте максимальну кількість спроб:
let attempts = 0;
const maxAttempts = 5;
let isSuccessful = false;
while (!isSuccessful && attempts < maxAttempts) {
attempts += 1;
console.log(`Спроба ${attempts}`);
if (attempts === 3) {
isSuccessful = true;
}
}
if (isSuccessful) {
console.log("Операцію виконано успішно");
} else {
console.log("Досягнуто максимальну кількість спроб");
}Навіть якщо isSuccessful ніколи не стане true, цикл завершиться після п’яти спроб.
Цикли з відомою кількістю повторень краще писати через for:
const names = ["Олена", "Андрій", "Марія"];
for (let index = 0; index < names.length; index += 1) {
console.log(names[index]);
}Коли кількість повторень залежить від умови, зручно використовувати while:
let fuel = 3;
while (fuel > 0) {
console.log("Виконуємо операцію");
fuel -= 1;
}Вибір відповідного типу циклу допомагає зробити логіку зрозумілішою та зменшує ризик помилки.
Порівняння <, <=, > і >= дають різні результати.
for (let number = 1; number < 4; number += 1) {
console.log(number);
}Результат:
1
2
3А цей варіант виведе ще й 4:
for (let number = 1; number <= 4; number += 1) {
console.log(number);
}Під час проєктування циклу перевірте:
що відбувається на першій ітерації;
що відбувається на останній ітерації;
що відбувається одразу після останньої ітерації;
чи може значення пропустити потрібну межу.
Якщо програма зависла, перевірте такі місця:
Тимчасово виводьте змінну, яка впливає на завершення:
let number = 1;
while (number <= 3) {
console.log("Поточне значення:", number);
number += 1;
}Якщо в консолі постійно з’являється одне й те саме значення, змінна не оновлюється або оновлюється неправильно.
Переконайтеся, що рядок на кшталт number += 1:
справді виконується;
не пропускається через continue;
не змінює іншу змінну;
не повертає лічильник до попереднього значення.
Іноді цикл не завершується через неочікуване початкове значення:
let number = 10;
while (number < 5) {
console.log(number);
number += 1;
}Цей цикл не виконається жодного разу, бо умова одразу хибна.
Інша помилка — початкове значення може бути несумісним із напрямком зміни:
let number = 10;
while (number > 5) {
console.log(number);
number += 1;
}Число збільшується, хоча для завершення потрібно його зменшувати.
Нескінченний цикл може бути не лише зовнішнім:
for (let row = 0; row < 3; row += 1) {
let column = 0;
while (column < 2) {
console.log(row, column);
column += 1;
}
}У вкладених циклах окремо перевіряйте лічильники кожного рівня.
JavaScript у браузері виконує синхронний код в одному основному потоці, який також відповідає за обробку подій та оновлення інтерфейсу.
Тому такий код заблокує сторінку:
while (true) {
// Небезпечний код: браузер не зможе перейти далі
}Не запускайте подібний приклад у консолі браузера без готовності примусово закрити вкладку або зупинити виконання.
Якщо потрібно виконувати роботу частинами, можна відкласти наступну ітерацію за допомогою setTimeout:
let number = 1;
function processNextNumber() {
if (number > 3) {
console.log("Готово");
return;
}
console.log(number);
number += 1;
setTimeout(processNextNumber, 0);
}
processNextNumber();У цьому прикладі між кроками браузер отримує можливість обробити інші події. Такий підхід не замінює правильну умову завершення, але допомагає не блокувати інтерфейс під час довгої роботи.
let index = 0;
while (index < 10) {
console.log(index);
// Помилка: index не змінюється
}Потрібно додати зміну:
index += 1;let index = 10;
while (index > 0) {
console.log(index);
index += 1;
}Для досягнення нуля значення потрібно зменшувати:
let index = 10;
while (index > 0) {
console.log(index);
index -= 1;
}continue пропускає оновленняЯкщо зміна лічильника розташована після continue, вона може не виконатися. Переконайтеся, що лічильник оновлюється до пропуску ітерації або в іншому гарантованому місці.
breakbreak корисний, але велика кількість прихованих виходів ускладнює читання коду. Якщо можливо, описуйте умову завершення безпосередньо в заголовку циклу.
Цикл, який чекає на успіх, відповідь або зміну стану, повинен мати запасний сценарій:
максимальну кількість спроб;
обмеження часу;
повідомлення про помилку;
можливість скасування операції.
Знайдіть помилку в коді:
let count = 5;
while (count > 0) {
console.log(count);
count += 1;
}Значення count збільшується, хоча умова вимагає, щоб воно стало меншим або дорівнювало нулю.
Виправлений варіант:
let count = 5;
while (count > 0) {
console.log(count);
count -= 1;
}
console.log("Старт!");Результат:
5
4
3
2
1
Старт!Нескінченний цикл не може досягти стану, у якому його умова стане false.
Найчастіша причина — змінна в умові не змінюється або змінюється в неправильному напрямку.
У кожному циклі потрібно визначити початкове значення, умову та крок зміни.
break дає змогу завершити цикл достроково.
continue може випадково пропустити зміну лічильника.
Для циклів, що залежать від зовнішніх подій, варто встановлювати максимальну кількість спроб.
Нескінченний синхронний цикл може заблокувати програму або браузер.
Під час налагодження перевіряйте поточні значення змінних і гарантований шлях до завершення.