Серия «JS черновики»

1

Когда код наконец-то работает на мобильном: история про баг, усталость и напарника из будущего⁠⁠

Серия JS черновики

Бывают такие вечера, когда ты уже лет 10 не "программист", а просто уставший человек, которому очень нужно, чтобы программка заработала именно так, как ты задумал. Смотришь на неё и думаешь: "Ну что тут может быть сложного? Две минуты, таймер, перезагрузка…" Всё ок, но она как положено не работает на смартфоне...

Для тех, кто не пишет код каждый день, поясню: это как если бы ты настроил будильник на телефоне, а он, когда телефон лежит в кармане, просто не срабатывает. Не ломается — а именно "засыпает", потому что телефон экономит заряд. Это вшито в его функционал. Но от понимания этого не легче — нужно что-то сделать, чтобы всё заработало так, как задумано.

Вот и у меня была такая штука: мне нужен был простой таймер, который должен был отсчитать 2 минуты и перезагрузить конкретную страницу, и так до бесконечности. На компьютере работало всё идеально. А на мобильном, как только я сворачивал браузер в фон, при пробуждении меня ожидала тишина и — вместо обновления — новый отсчет. Цифры в "спящем режиме" замирали, будто время остановилось. Сначала я проверял этот глюк на разных телефонах, потом на разных браузерах: "что мертво, умереть не может"... На Андроидах в фоне таймеры специально замедляют? Это же базовая штука... Казалось бы, да. Но иногда такие мелочи просто из-за долгого отсутствия практики ускользают.

Открыл окошко с ИИ и не как обычно кинул написанный текст и написал "исправь ошибки". А обратился как к напарнику, который просто посидит рядом, посмотрит код, и, пока я делаю себе кофе и расслабленно жую плюшку, выдаст решение без усилий с моей стороны: мол, "смотри, вот тут косяк". Отправил в поле для запроса скрипт и честно написал: "После активации вкладки на мобильных, вместо обновления запускается новый отсчет. Проанализируй".

И началось то самое тихое чудо. Не фейерверк, не "вау, магия", а нормальный разговор. Сначала — объяснение: браузер душит таймеры, чтобы экономить батарею, и на мобильном это критично. Потом — идея: не считать время по тикам, а запоминать момент старта и сравнивать реальное время. Таймер оставить только для красивой цифры на экране, а настоящую проверку делать в момент, когда пользователь возвращается на вкладку. И сразу результат — новый и готовый скрипт, который можно вставить и проверить.

Вставил его на страницу и запустил на телефоне, свернул, перезагрузил устройство. Цифры бегут. Время идёт. Вкладка уходит в фон — таймер на экране замирает (чтобы не тратить ресурсы), но реальное время никуда не девается. И жестко установленные две минуты прошли, страница обновляется. И вот этот момент, когда ты видишь, что наконец-то всё работает и на компьютере, и на мобильном, — появляется одна мысль: "А что, так можно было?".

В голову сами собой лезут фильмы из детства: "Терминатор" и "Матрица". В "Терминаторе" машина — это абсолютный исполнитель: ты даёшь команду — она делает. Без вопросов. Для многих из нас именно так и выглядело программирование раньше: ты говоришь, компьютер слушается. Точка.

А "Матрица" — это про границы. Про то, как привычная реальность оказывается слоем абстракции, и, если копнуть глубже, там совсем другая логика. И вот этот переход от «я один знаю, как надо» к «давай разберём вместе, где подвох» — он как раз оттуда. Как будто ты спустился на уровень ниже и теперь видишь не только команды, но и среду, в которой они работают. И у тебя есть напарник, который тоже это видит.

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

В этом и есть самое приятное. Не в том, что ИИ всё починил, а в том, что он помог тебе самому увидеть решение. Как будто рядом сел опытный коллега с наметанным глазом, ткнул пальцем в строчку и просто сказал: "Смотри сюда".

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

<script type="text/javascript">

const DURATION_MS = 2 * 60 * 1000; // 2 минуты в миллисекундах

const startTime = Date.now();

let countdownTimer;

function updateCountdown() {

const elapsed = Date.now() - startTime;

const remainingMs = Math.max(0, DURATION_MS - elapsed);

const seconds = Math.ceil(remainingMs / 1000);

const span = document.querySelector('#countdown span');

if (span) {span.textContent = seconds;}

// Если время вышло — перезагружаем

if (remainingMs <= 0) {stopCountdown(); window.location.reload();}}

function startCountdown() {if (countdownTimer) return; countdownTimer = setInterval(updateCountdown, 1000); updateCountdown(); // сразу обновить без ожидания 1 секунды

}

function stopCountdown() {clearInterval(countdownTimer); countdownTimer = null;}

document.addEventListener('visibilitychange', () => {

if (!document.hidden) {// Вкладка стала активной: сначала проверяем, не истекло ли время

const elapsed = Date.now() - startTime;

if (elapsed >= DURATION_MS) {console.log('2 минуты прошли, перезагружаем при активации');

window.location.reload();

return;}// Если время ещё не вышло — запускаем/продолжаем отсчёт

startCountdown();} else {// Вкладку свернули — останавливаем тиканье таймера (экономим ресурсы)

stopCountdown();}});

// Если страница сразу открыта в активной вкладке — запускаем отсчёт

if (!document.hidden) {startCountdown();}

</script>

Показать полностью

Как победил лаги в таймере: разбор полетов⁠⁠

Серия JS черновики

Люблю, когда в коде нет магии, а есть понятная логика: "вот почему так, вот откуда взялось". Для меня ИИ — не про "дай готовый код", а про "давай разберём, где тут лишнее и что реально нужно".

Продолжаю возиться с таймером, вышло как у многих: на коленке набросанные 10 строк, потом тестирование в поле подкинуло кейсов (вкладки в фоне, кеш, выгрузка браузера), и код распух до 136 строк, превратившись в кашу из костылей. Нужно было оптимизировать и доработать. Так что с помощью ИИ из кода вытащил логику работы (алгоритм) и все это дело переписал, видя всю картину и уложившись в 39 строк с сохранением ресурсов памяти, процессорного времени и без потери функционала.

Что особенного:

  • Не даёт крутить карты лояльности в фоне: при сворачивании вкладки таймер ставится на паузу и не тикает впустую.

  • Честно досчитывает остаток: даже если браузер выгружался из памяти, после возврата отсчёт продолжается с той же точки.

  • Не показывает одну карту дважды: при смене cardId таймер сбрасывается.

  • Работает стабильно: минимум обращений к localStorage, защита от ошибок DOM.

Как это устроено:

  • Старт. При загрузке один раз читаем localStorage. Если cardId не совпадает или данных нет — прописываем новый старт, иначе продолжаем отсчет с сохранённого времени.

  • Тик. Каждую секунду считаем остаток от startTime и выводим его в <span>. К localStorage больше не лезем.

  • Пауза по вкладке. На visibilitychange ставим интервал на паузу, startTime не трогаем — при возврате честно досчитываем остаток секунд.

  • Конец таймера. Останавливаем интервал, чистим localStorage, прячем карту, показываем сообщение и кнопку для показа следующей.

  • Кнопка. Сбрасывает localStorage и перезагружает страницу — таймер стартует заново.

  • Защита. Если соле для счетчика #countdown span не найдено — скрипт не падает, просто не обновляет цифру.

Итоговый скрипт (39 строк)

<button id="refresh-card-btn" data-card-id="12345">Получить новую карту</button>
<h1 id="countdown"><span>120</span> сек. до загрузки другой карты</h1>
<div id="qrcode"></div>
<h2 id="сr_code"></h2>

<script type="text/javascript">
const DURATION = 120000;
const LS_START = 'timer_start_ms';
const LS_CARD = 'current_card_id';
let timer = null;
let startTime = null;
const btn = document.getElementById('refresh-card-btn');
const cardId = btn?.dataset?.cardId || '';
{
const savedStart = localStorage.getItem(LS_START);
const savedCard = localStorage.getItem(LS_CARD);
if (!savedStart || savedCard !== cardId) {
startTime = Date.now();
localStorage.setItem(LS_START, String(startTime));
localStorage.setItem(LS_CARD, cardId);
} else startTime = parseInt(savedStart, 10); }

function tick() {
const left = Math.max(0, DURATION - (Date.now() - startTime));
const span = document.querySelector('#countdown span');
if (span) span.textContent = Math.ceil(left / 1000);
if (left <= 0) {
clearInterval(timer);
timer = null;
localStorage.removeItem(LS_START);
localStorage.removeItem(LS_CARD);
const qr = document.getElementById('qrcode');
if (qr) qr.innerHTML = '';
const h1 = document.getElementById('countdown');
if (h1) h1.innerHTML = 'Для получения новой карты обновите страницу или нажмите кнопку “Получить новую карту”';
return; } }

function start() {if (timer) return; timer = setInterval(tick, 1000); tick();}

document.addEventListener('visibilitychange', () => {document.hidden ? clearInterval(timer, timer = null) : start();});
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start);
else start();
btn?.addEventListener('click', () => {
localStorage.removeItem(LS_START);
localStorage.removeItem(LS_CARD);
window.location.reload(); });
</script>

Показать полностью
Отличная работа, все прочитано!

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества

Недвижимость и ремонт

Теги

Популярные авторы

Сообщества