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