Repository: vercel/next.js
File: packages/next/src/server/app-render/app-render.tsx (TypeScript)

Code before:
    () => {
      if (cacheSignal.hasPendingReads()) {
        initialAbandonController.abort()
      } else {
        initialStageController.advanceStage(RenderStage.Runtime)
      }
    },
    () => {
      if (cacheSignal.hasPendingReads()) {
        initialAbandonController.abort()
      } else {
        initialStageController.advanceStage(RenderStage.Dynamic)
      }
    }
  )

  if (initialStageController.currentStage !== RenderStage.Abandoned) {
    // No cache misses. We can use the result as-is.
    return {
      stream: initialStreamResult.stream,
      accumulatedChunksPromise: initialStreamResult.accumulatedChunksPromise,
      staticInterruptReason: initialStageController.getStaticInterruptReason(),
      runtimeInterruptReason:
        initialStageController.getRuntimeInterruptReason(),
      startTime,
      staticStageEndTime: initialStageController.getStaticStageEndTime(),
      runtimeStageEndTime: initialStageController.getRuntimeStageEndTime(),
      debugChannel,
      requestStore,
    }
  } else {
    // The render was abandoned due to cache misses.
    // Clean up the unused streams to avoid leaking resources.
    initialStreamResult.stream.cancel()
    initialStreamResult.accumulatedChunksPromise.catch(() => {})
  }

  if (process.env.__NEXT_DEV_SERVER && setCacheStatus) {
    setCacheStatus('filling', htmlRequestId)
  }

  // Cache miss. We will use the initial render to fill caches, and discard its result.
  // Then, we can render again with warm caches.

  // TODO(restart-on-cache-miss):
  // This might end up waiting for more caches than strictly necessary,
  // because we can't abort the render yet, and we'll let runtime/dynamic APIs resolve.
  // Ideally we'd only wait for caches that are needed in the static stage.
  // This will be optimized in the future by not allowing runtime/dynamic APIs to resolve.

  await cacheSignal.cacheReady()

Review comment (performance):
i'm not sure if this is semantically equivalent to what we had before (but haven't come up with a counterexample yet). feels like it'd be safer to track if we've aborted before and do sth like
```tsx
      if (didAbort || initialStageController.currentStage === RenderStage.Abandoned) {
        return
      }
      if (cacheSignal.hasPendingReads()) {
        initialAbandonController.abort()
        return
      }
      initialStageController.advanceStage(RenderStage.Dynamic)
```      

i guess i'm not confident that if `hasPendingReads()` was true a task ago, it'll still be true a task later?

Code after:
        stream.cancel()
      })

      return {
        stream,
        accumulatedChunksPromise,
      }
    },
    () => {
      if (initialAbandonController.signal.aborted === true) {
        return
      } else if (cacheSignal.hasPendingReads()) {
        initialAbandonController.abort()
      } else {
        initialStageController.advanceStage(RenderStage.Runtime)
      }
    },
    () => {
      if (initialAbandonController.signal.aborted === true) {
        return
      } else if (cacheSignal.hasPendingReads()) {
        initialAbandonController.abort()
      } else {
        initialStageController.advanceStage(RenderStage.Dynamic)
      }
    }
  )

  if (initialStageController.currentStage !== RenderStage.Abandoned) {
    // No cache misses. We can use the result as-is.
    return {
      stream: initialStreamResult.stream,
      accumulatedChunksPromise: initialStreamResult.accumulatedChunksPromise,
      staticInterruptReason: initialStageController.getStaticInterruptReason(),
      runtimeInterruptReason:
        initialStageController.getRuntimeInterruptReason(),
      startTime,
      staticStageEndTime: initialStageController.getStaticStageEndTime(),
      runtimeStageEndTime: initialStageController.getRuntimeStageEndTime(),
      debugChannel,
      requestStore,
    }
  }

  if (process.env.__NEXT_DEV_SERVER && setCacheStatus) {
    setCacheStatus('filling', htmlRequestId)
  }

  // Cache miss. We will use the initial render to fill caches, and discard its result.
  // Then, we can render again with warm caches.