Repository: angular/angular
File: packages/core/src/render3/reactivity/effect.ts (TypeScript)

Code before:

  return effectRef;
}

export interface EffectNode extends BaseEffectNode, SchedulableEffect {
  cleanupFns: EffectCleanupFn[] | undefined;
  injector: Injector;
  notifier: ChangeDetectionScheduler;

  onDestroyFns: (() => void)[] | null;
}

export interface ViewEffectNode extends EffectNode {
  view: LView;
}

export interface RootEffectNode extends EffectNode {
  scheduler: EffectScheduler;
}

export const EFFECT_NODE: Omit<EffectNode, 'fn' | 'destroy' | 'injector' | 'notifier'> =
  /* @__PURE__ */ (() => ({
    ...BASE_EFFECT_NODE,
    cleanupFns: undefined,
    zone: null,
    onDestroyFns: [],
    run(this: EffectNode): void {
      if (ngDevMode && isInNotificationPhase()) {
        throw new Error(`Schedulers cannot synchronously execute watches while scheduling.`);
      }
      // We clear `setIsRefreshingViews` so that `markForCheck()` within the body of an effect will
      // cause CD to reach the component in question.
      const prevRefreshingViews = setIsRefreshingViews(false);
      try {
        runEffect(this);
      } finally {
        setIsRefreshingViews(prevRefreshingViews);
      }
    },

    cleanup(this: EffectNode): void {
      if (!this.cleanupFns?.length) {
        return;
      }
      const prevConsumer = setActiveConsumer(null);
      try {
        // Attempt to run the cleanup functions. Regardless of failure or success, we consider
        // cleanup "completed" and clear the list for the next run of the effect. Note that an error
        // from the cleanup function will still crash the current run of the effect.
        while (this.cleanupFns.length) {
          this.cleanupFns.pop()!();

Review comment (bug):
I'm not a fan of having to turn this into an array, but the alternative would be to do something like this in the debug data code which feels a bit hacky:

```
const originalOnDestroy = node.onDestroyFn;

node.onDestroyFn = () => {
 cleanupDebugData();
 originalOnDestroy();
};
```

Code after:

  return effectRef;
}

export interface EffectNode extends BaseEffectNode, SchedulableEffect {
  cleanupFns: EffectCleanupFn[] | undefined;
  injector: Injector;
  notifier: ChangeDetectionScheduler;

  onDestroyFns: (() => void)[] | null;
}

export interface ViewEffectNode extends EffectNode {
  view: LView;
}

export interface RootEffectNode extends EffectNode {
  scheduler: EffectScheduler;
}

export const EFFECT_NODE: Omit<EffectNode, 'fn' | 'destroy' | 'injector' | 'notifier'> =
  /* @__PURE__ */ (() => ({
    ...BASE_EFFECT_NODE,
    cleanupFns: undefined,
    zone: null,
    onDestroyFns: null,
    run(this: EffectNode): void {
      if (ngDevMode && isInNotificationPhase()) {
        throw new Error(`Schedulers cannot synchronously execute watches while scheduling.`);
      }
      // We clear `setIsRefreshingViews` so that `markForCheck()` within the body of an effect will
      // cause CD to reach the component in question.
      const prevRefreshingViews = setIsRefreshingViews(false);
      try {
        runEffect(this);
      } finally {
        setIsRefreshingViews(prevRefreshingViews);
      }
    },

    cleanup(this: EffectNode): void {
      if (!this.cleanupFns?.length) {
        return;
      }
      const prevConsumer = setActiveConsumer(null);
      try {
        // Attempt to run the cleanup functions. Regardless of failure or success, we consider
        // cleanup "completed" and clear the list for the next run of the effect. Note that an error
        // from the cleanup function will still crash the current run of the effect.
        while (this.cleanupFns.length) {
          this.cleanupFns.pop()!();