()
| 409 | } |
| 410 | |
| 411 | private async loadEffect(): Promise<void> { |
| 412 | const extRequest = this.extRequest(); |
| 413 | |
| 414 | // Capture the previous status before any state transitions. Note that this is `untracked` since |
| 415 | // we do not want the effect to depend on the state of the resource, only on the request. |
| 416 | const {status: currentStatus, previousStatus} = untracked(this.state); |
| 417 | |
| 418 | if (extRequest.request === undefined) { |
| 419 | // Nothing to load (and we should already be in a non-loading state). |
| 420 | return; |
| 421 | } else if (currentStatus !== 'loading') { |
| 422 | // We're not in a loading or reloading state, so this loading request is stale. |
| 423 | return; |
| 424 | } |
| 425 | |
| 426 | // Cancel any previous loading attempts. |
| 427 | this.abortInProgressLoad(); |
| 428 | |
| 429 | // Capturing _this_ load's pending task in a local variable is important here. We may attempt to |
| 430 | // resolve it twice: |
| 431 | // |
| 432 | // 1. when the loading function promise resolves/rejects |
| 433 | // 2. when cancelling the loading operation |
| 434 | // |
| 435 | // After the loading operation is cancelled, `this.resolvePendingTask` no longer represents this |
| 436 | // particular task, but this `await` may eventually resolve/reject. Thus, when we cancel in |
| 437 | // response to (1) below, we need to cancel the locally saved task. |
| 438 | let resolvePendingTask: (() => void) | undefined = (this.resolvePendingTask = |
| 439 | this.pendingTasks.add()); |
| 440 | |
| 441 | const {signal: abortSignal} = (this.pendingController = new AbortController()); |
| 442 | |
| 443 | try { |
| 444 | // The actual loading is run through `untracked` - only the request side of `resource` is |
| 445 | // reactive. This avoids any confusion with signals tracking or not tracking depending on |
| 446 | // which side of the `await` they are. |
| 447 | const stream = untracked(() => { |
| 448 | return this.loaderFn({ |
| 449 | params: extRequest.request as Exclude<R, undefined>, |
| 450 | abortSignal, |
| 451 | previous: { |
| 452 | status: previousStatus, |
| 453 | }, |
| 454 | }); |
| 455 | }); |
| 456 | |
| 457 | // If this request has been aborted, or the current request no longer |
| 458 | // matches this load, then we should ignore this resolution. |
| 459 | const shouldDiscard = () => abortSignal.aborted || untracked(this.extRequest) !== extRequest; |
| 460 | |
| 461 | if (isSignal(stream)) { |
| 462 | if (shouldDiscard()) { |
| 463 | return; |
| 464 | } |
| 465 | |
| 466 | this.state.set({ |
| 467 | extRequest, |
| 468 | status: 'resolved', |
nothing calls this directly
no test coverage detected