Repository: facebook/react
File: packages/react-server/src/ReactFlightServer.js (JavaScript)

Code before:
            rejectedThenable.status = 'rejected';
            rejectedThenable.reason = error;
          }
        },
      );
      break;
    }
  }

  thenable.then(
    value => {
      newTask.model = value;
      pingTask(request, newTask);
    },
    reason => {
      if (newTask.status === PENDING) {
        // We expect that the only status it might be otherwise is ABORTED.
        // When we abort we emit chunks in each pending task slot and don't need
        // to do so again here.
        erroredTask(request, newTask, reason);
        enqueueFlush(request);

        if (request.abortableTasks.size === 0) {
          const onAllReady = request.onAllReady;
          onAllReady();
        }
      }
    },
  );

  if (enableProfilerTimer && enableComponentPerformanceTrack) {
    // If this is async we need to time when this task finishes.
    newTask.timed = true;
  }

  return newTask.id;
}

function serializeReadableStream(
  request: Request,
  task: Task,
  stream: ReadableStream,
): string {
  // Detect if this is a BYOB stream. BYOB streams should be able to be read as bytes on the
  // receiving side. It also implies that different chunks can be split up or merged as opposed
  // to a readable stream that happens to have Uint8Array as the type which might expect it to be
  // received in the same slices.
  // $FlowFixMe: This is a Node.js extension.
  let supportsBYOB: void | boolean = stream.supportsBYOB;
  if (supportsBYOB === undefined) {
    try {

Review comment (bug):
The bug mentioned also affects prerenders where the last task to complete is a thenable that rejects right? because in performWork the onAllReady would never be called and the rejection would lead to the erroredTask call above which needs to hit this special case where onAllReady is called to work right?

There are actually a number of places where this same flaw likely exists like with serializing streams.

It seems like maybe we ought to move the onAllReady call into retryTask, erroredTask, and abort so that we only ever call it once

Code after:
            rejectedThenable.status = 'rejected';
            rejectedThenable.reason = error;
          }
        },
      );
      break;
    }
  }

  thenable.then(
    value => {
      newTask.model = value;
      pingTask(request, newTask);
    },
    reason => {
      if (newTask.status === PENDING) {
        // We expect that the only status it might be otherwise is ABORTED.
        // When we abort we emit chunks in each pending task slot and don't need
        // to do so again here.
        erroredTask(request, newTask, reason);
        enqueueFlush(request);
      }
    },
  );

  if (enableProfilerTimer && enableComponentPerformanceTrack) {
    // If this is async we need to time when this task finishes.
    newTask.timed = true;
  }

  return newTask.id;
}

function serializeReadableStream(
  request: Request,
  task: Task,
  stream: ReadableStream,
): string {
  // Detect if this is a BYOB stream. BYOB streams should be able to be read as bytes on the
  // receiving side. It also implies that different chunks can be split up or merged as opposed
  // to a readable stream that happens to have Uint8Array as the type which might expect it to be
  // received in the same slices.
  // $FlowFixMe: This is a Node.js extension.
  let supportsBYOB: void | boolean = stream.supportsBYOB;
  if (supportsBYOB === undefined) {
    try {
      // $FlowFixMe[extra-arg]: This argument is accepted.
      stream.getReader({mode: 'byob'}).releaseLock();
      supportsBYOB = true;
    } catch (x) {
      supportsBYOB = false;