Repository: angular
Issue: Calling resource.set(value) does not abort in-progress loaders if the given value is equal to the value before the resource was reloaded
Labels: area: core,state: has PR,bug,cross-cutting: signals

### Which @angular/* package(s) are the source of the bug?

core

### Is this a regression?

No

### Description

I'm not sure whether this is a bug or whether this is intended, but it feels unintuitive to me.  Therefore "expected" below is what _I_ would have expected.

**Expected behaviour**

Calling `resouce.set(value)` should always abort in-progress async loading

**Actual behaviour**

Calling `resouce.reload()` then calling `resouce.set(value)` _before_ the async reload is complete, with the same `value` that was set for the resource before reload was called, does not abort the reload operation.

This creates an inconsistent behaviour.  E.g.:

```ts
resource.set({});
resources.reload();
resource.set(undefined); // cancels the reload


resource.set(undefined);
resources.reload();
resource.set(undefined); // does not cancel the reload
```

**Example reproduction**

In the following component, click the "Load it" button to trigger the reload, then click the "Cancel it" button to set the resource explicitly to `undefined` before the async load operation has completed.

```ts
import {Component, resource, ResourceStatus} from '@angular/core';
import {JsonPipe} from '@angular/common';

@Component({
  selector: 'app-root',
  imports: [JsonPipe],
  template: `
    <p>
      @if (something.value()) {
        {{something.value() | json}}
      } @else if (something.isLoading()) {
        Loading...
      } @else {
        Load it?
      }
    </p>
    @if (something.isLoading()) {
      <p><button type="button" (click)="cancelIt()">Cancel it</button></p>
    } @else {
      <p><button type="button" (click)="loadIt()">Load it</button></p>
    }
  `
})
export class AppComponent {
  readonly something = resource({
    loader: async ({previous, abortSignal}) => {
      if (previous.status === ResourceStatus.Idle) {
        return undefined;
      }
      return await someLongRunningTask(abortSignal);
    }
  });

  loadIt() {
    this.something.reload();
  }

  cancelIt() {
    // Setting a value after reload is called that is the same as the value
    // which was set before reload was called does not cancel the reload action
    this.something.set(undefined);

    // whereas setting a different value to the previous one always aborts in-progress reload operations
    // this.something.set({});
  }
}

function someLongRunningTask(abortSignal: AbortSignal) {
  return new Promise<unknown>((resolve, reject) => {
    if (abortSignal.aborted) {
      reject(abortSignal.reason);
      return;
    }

    let timeout: number | undefined = undefined;
    const abort = () => {
      clearTimeout(timeout);
      reject(abortSignal.reason);
    };
    abortSignal.addEventListener('abort', abort);
    timeout = setTimeout(() => {
      abortSignal.removeEventListener('abort', abort);
      resolve({success: true});
    }, 2_000)
  })
}
```

### Please provide a link to a minimal reproduction of the bug

_No response_

### Please provide the exception or error you saw

```true

```

### Please provide the environment you discovered this bug in (run `ng version`)

```true
Angular CLI: 19.0.6
Node: 20.18.1
Package Manager: npm 10.8.2
OS: darwin arm64

Angular: 19.0.5
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1900.6
@angular-devkit/build-angular   19.0.6
@angular-devkit/core            19.0.6
@angular-devkit/schematics      19.0.6
@angular/cli                    19.0.6
@schematics/angular             19.0.6
rxjs                            7.8.1
typescript                      5.6.3
zone.js                         0.15.0
```

### Anything else?

_No response_