Метод cancel

Функция cancel останавливает автоматическое обновление элементов, ранее запущенное через render. Это необходимо для предотвращения утечек памяти и прекращения ненужных обновлений.


Сигнатура

cancel(nodes?: Element | NodeList | Element[] | HTMLCollectionOf<Element>): void

Параметр nodes — необязательный.

  • Если передан — останавливает таймеры только для указанных элементов.
  • Если не передан — останавливает все активные таймеры.

Остановка всех обновлений

import { cancel } from 'timeago.js';

cancel(); // останавливает все активные render-таймеры

Это удобно для SPA-переходов между страницами.


Остановка конкретного элемента

import { cancel } from 'timeago.js';

const el = document.getElementById('post-time');

cancel(el);

Остановка набора элементов

import { cancel } from 'timeago.js';

const nodes = document.querySelectorAll('.time');

cancel(nodes);

Паттерн: render + cancel

Стандартный жизненный цикл:

import { render, cancel } from 'timeago.js';

const nodes = document.querySelectorAll('.time');

// Запуск
render(nodes, 'ru');

// Остановка при уходе со страницы
window.addEventListener('beforeunload', () => {
  cancel(nodes);
});

cancel в компонентах React

import { useEffect, useRef } from 'react';
import { render, cancel } from 'timeago.js';

function TimeLabel({ date }: { date: string }) {
  const ref = useRef<HTMLTimeElement>(null);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;

    render(el, 'ru');

    // cancel вызывается при размонтировании компонента
    return () => cancel(el);
  }, [date]);

  return <time ref={ref} dateTime={date} />;
}

Возврат функции из useEffect — стандартный механизм cleanup в React.


cancel в Vue

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { render, cancel } from 'timeago.js';

const props = defineProps({ date: String });
const el = ref(null);

onMounted(() => render(el.value, 'ru'));
onBeforeUnmount(() => cancel(el.value));
</script>

onBeforeUnmount гарантирует вызов cancel перед разрушением компонента.


cancel в Angular

import { Component, ElementRef, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { render, cancel } from 'timeago.js';

@Component({
  selector: 'app-time',
  template: '<time #timeEl [attr.datetime]="date"></time>',
})
export class TimeComponent implements OnInit, OnDestroy {
  date = '2025-05-26T10:00:00Z';

  @ViewChild('timeEl') timeEl!: ElementRef;

  ngOnInit() {
    render(this.timeEl.nativeElement, 'ru');
  }

  ngOnDestroy() {
    cancel(this.timeEl.nativeElement);
  }
}

Почему cancel важен

Без cancel после удаления элементов из DOM:

  • Таймеры продолжают работать в памяти.
  • Попытки обновить удалённые элементы могут вызвать ошибки.
  • В SPA-приложениях это накапливается с каждым переходом.

В браузере с WeakMap некоторые ситуации обрабатываются автоматически (GC очистит ссылку), но явный cancel надёжнее и не зависит от реализации.


cancel и повторный render

Правильная последовательность при обновлении данных:

import { cancel, render } from 'timeago.js';

function refresh(nodes, locale) {
  cancel(nodes); // сначала остановить старые таймеры
  render(nodes, locale); // затем запустить новые
}

Без cancel будут работать одновременно старые и новые таймеры.


Проверка безопасности при cancel

import { cancel } from 'timeago.js';

function safeCancell(nodes) {
  if (!nodes) return;

  if (nodes instanceof NodeList || nodes instanceof HTMLCollection) {
    if (nodes.length === 0) return;
  }

  cancel(nodes);
}

cancel при смене локали

import { cancel, render } from 'timeago.js';

function changeLocale(newLocale) {
  const nodes = document.querySelectorAll('[data-timeago]');
  cancel(nodes);        // остановить обновления с предыдущей локалью
  render(nodes, newLocale); // перезапустить с новой
}

cancel при скрытии элементов

Если элементы временно скрываются (например, в аккордеоне или вкладках), можно приостановить обновления:

import { cancel, render } from 'timeago.js';

function hideTab(tabContent) {
  const nodes = tabContent.querySelectorAll('.time');
  cancel(nodes);
}

function showTab(tabContent) {
  const nodes = tabContent.querySelectorAll('.time');
  render(nodes, 'ru');
}