Как очистить сигнал, который не используется в Angular 16?

В RxJS несколько наблюдаемых объектов имеют функцию очистки, запускаемую при отказе от подписки, например:timer(). Я пытаюсь понять, как лучше всего реализовать это в чистом сигнале. Некоторые из моих попыток на stackblitz.

В приведенном ниже коде показано, как можно реализовать таймер с нуля в RxJS:

      function getTimerRxjs(frequency: number): Observable<number> {
  // Below code is equivalent to timer(frequency)
  return new Observable((observer) => {
    let value = 0;
    let lastTimeout;
    const loop = () => {
      console.log('getTimerRxjs loop is running');
      observer.next(value);
      value += 1;
      lastTimeout = setTimeout(loop, frequency);
    };
    lastTimeout = setTimeout(loop, frequency);
    return () => {
      if (lastTimeout) clearTimeout(lastTimeout);
    };
  });
}

Вариант А. Пытаясь воспроизвести подобное поведение, вы можете передать DestroyRef в функцию, генерирующую таймер, следующим образом:

      function getTimerWithRef(frequency: number, destroyRef: DestroyRef): Signal<number> {
  const timer = signal(-1);
  let lastTimeout;
  const loop = () => {
    console.log('getTimerWithRef loop is running');
    timer.update((value) => value + 1);
    lastTimeout = setTimeout(loop, frequency);
  };
  lastTimeout = setTimeout(loop, frequency);
  destroyRef.onDestroy(() => {
    if (lastTimeout) clearTimeout(lastTimeout);
  });
  return timer;
}

Вариант Б. Вы можете ввести в функцию DestroyRef во время выполнения следующим образом:

      function getTimerAutoCleanup(frequency: number): Signal<number> {
  const timer = signal(-1);
  let lastTimeout;
  const loop = () => {
    console.log('getTimerAutoCleanup loop is running');
    timer.update((value) => value + 1);
    lastTimeout = setTimeout(loop, frequency);
  };
  lastTimeout = setTimeout(loop, frequency);
  inject(DestroyRef).onDestroy(() => {
    if (lastTimeout) clearTimeout(lastTimeout);
  });
  return timer;
}

Хотя вариант Б кажется элегантным, я опасаюсь, чтоinject()вызов может не разрешиться в правильный контекст.

  • Если создать этот сигнал из@Injectable(), будет лиinject(DestroyRef)разрешить компоненту или службе?
  • Существуют ли другие риски использования варианта Б , когда некоторые ошибки внедрения могут проявиться только во время выполнения?

Мне нужна помощь, чтобы найти, какой вариант будет более идиоматическим в этом контексте.

1 ответ

Взгляните на решение, использованное командой angular вtoSignal()функция. Эта функция преобразует rxJS Observable в сигнал, а также включает в себя функцию очистки. Короче говоря, их основным решением является вариант Б, но они предоставляют возможность внедрить собственный инжектор (который, в свою очередь, вводит DestroyRef, как вариант А).

https://angular.io/api/core/rxjs-interop/toSignal

      const cleanupRef =
      requiresCleanup ? options?.injector?.get(DestroyRef) ?? inject(DestroyRef) : null;

https://github.com/angular/angular/blob/237d90f4e38f528ae20aeedd09894aa63fb9de14/packages/core/rxjs-interop/src/to_signal.ts#L163

По поводу вашего беспокойства о правильном контексте не забывайте, что звонить можно толькоinject()из контекста инъекции, который затем должен автоматически использовать правильный контекст, но это означает, что при вызове функции в любом другом месте вам придется самостоятельно указать правильный инжектор, либо предоставив его, либо используяrunInInjectionContext(injector, () => {})(EnvironmentInjectorчлен устарел в пользу автономной версии).

https://angular.io/errors/NG0203

https://angular.io/api/core/runInInjectionContext

Другие вопросы по тегам