Перейти к содержанию

Задержка сигналов через debounced

📅 30.09.2026

debounced — экспериментальная возможность. Её уже можно пробовать, но до стабильной версии поведение может измениться.

debounced откладывает реакцию на значение сигнала, пока оно не перестанет меняться. Функция возвращает Resource, чьё значение — это отложенное значение исходного сигнала.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import {debounced, resource, signal} from '@angular/core';

@Component({
  template: `
    <input (input)="query.set($event.target.value)" />

    @if (results.isLoading()) {
      <p>Searching…</p>
    }
    @for (item of results.value(); track item.id) {
      <li>{{ item.name }}</li>
    }
  `,
})
export class Search {
  query = signal('');

  debouncedQuery = debounced(this.query, 300);

  results = resource({
    params: () => this.debouncedQuery.value(),
    loader: ({params}) => fetchResults(params),
  });
}

debounced принимает исходный сигнал и паузу в миллисекундах. У полученного ресурса value() всегда хранит последнее установившееся значение, а status() показывает, ждёт ли ресурс новое.

Статус во время задержки

Пока идёт отсчёт таймера, status() равен 'loading', а value() возвращает прежнее полученное значение. Когда таймер истекает, ресурс устанавливается в 'resolved'. Если исходный сигнал выбрасывает исключение, ресурс сразу переходит в 'error', таймер не запускается.

Полный список статусов и то, что при них возвращает value(), — в разделе Статус ресурса.

Своя функция ожидания

Вместо миллисекунд можно передать функцию, которая возвращает Promise<void>. Ресурс завершается, когда завершается промис. Если исходный сигнал изменится до завершения промиса, Angular отбрасывает прежний промис и начинает новый.

1
2
3
4
5
6
7
debouncedQuery = debounced(query, (value, lastSnapshot) => {
  // Retry immediately after an error rather than making the user wait again.
  if (lastSnapshot.status === 'error') return;
  // Short queries get a longer delay—the user is likely still typing.
  const ms = value.length < 3 ? 500 : 200;
  return new Promise<void>((resolve) => setTimeout(resolve, ms));
});

Тип DebounceTimer описан в справочнике API.

Равенство

По умолчанию debounced сравнивает значения через Object.is.

Если стандартная проверка идентичности слишком строгая, задайте свою функцию равенства параметром equal:

1
2
3
debouncedFilter = debounced(filter, 200, {
  equal: (a, b) => a.category === b.category && a.minPrice === b.minPrice,
});

Контекст инъекции

debounced вызывают только внутри контекста инъекции. Когда инжектор уничтожается, Angular сам уничтожает отложенный ресурс и отменяет таймер, который ещё не сработал.

Чтобы вызвать debounced вне контекста инъекции, передайте Injector явно в параметрах:

1
2
3
4
5
6
7
8
@Service()
export class SearchService {
  private injector = inject(Injector);

  createDebouncedQuery(query: Signal<string>): Resource<string> {
    return debounced(query, 300, {injector: this.injector});
  }
}

Источник: https://angular.dev/guide/signals/debounced

Комментарии