Repository: create-react-app
Issue: Compilation stuck at 'Files successfully emitted, waiting for typecheck results...' caused by race condition in TypeScript + web workers context
Labels: stale,needs triage,issue: bug report

### Describe the bug

This is a more specific version of bug #8707 with an attempt to provide a minimal reproducing example and a first cause analysis. Edit: Just found another existing issue for that https://github.com/facebook/create-react-app/issues/10315 

When working with loaders for web workers there seems to be a race condition in handling the TypeScript compilation, which can cause the compilation to get stuck at: "Files successfully emitted, waiting for typecheck results...".

### Did you try recovering your dependencies?

This has nothing to do with a corrupt dependency tree.

### Environment

```
Environment Info:

  current version of create-react-app: 4.0.3
  running from /home/fabian/.npm/_npx/c67e74de0542c87c/node_modules/create-react-app

  System:
    OS: Linux 4.15 Ubuntu 18.04.4 LTS (Bionic Beaver)
    CPU: (4) x64 Intel(R) Core(TM) i5-4670 CPU @ 3.40GHz
  Binaries:
    Node: 14.16.0 - ~/.nvm/versions/node/v14.16.0/bin/node
    Yarn: Not Found
    npm: 7.6.3 - ~/.nvm/versions/node/v14.16.0/bin/npm
  Browsers:
    Chrome: 80.0.3987.162
    Firefox: 87.0
  npmPackages:
    react: ^17.0.2 => 17.0.2 
    react-dom: ^17.0.2 => 17.0.2 
    react-scripts: 4.0.3 => 4.0.3 
  npmGlobalPackages:
    create-react-app: Not Found
```


### Steps to reproduce

I have setup a reproducing example [here](https://github.com/bluenote10/js-debug-projects/tree/master/cra_webworker). Since the problem involves a race condition, there is no 100% reproduction. In the reproduction example it is roughly 10%, in our real production code it is more like 80%.

The general pattern causing the problem involves loaders for web workers. In my example I'm using [`comlink-loader`](https://github.com/GoogleChromeLabs/comlink-loader), but issue #8707 shows cases involving other loaders like [`workerize-loader`](https://github.com/developit/workerize-loader).

**`index.tsx`**:

```ts
import React from "react";
import ReactDOM from "react-dom";

// No double compilation without these two lines:
import Worker from "./worker";
const worker = new Worker();

ReactDOM.render(
  <React.StrictMode>
    <div>Hello World</div>
  </React.StrictMode>,
  document.getElementById("root")
);

// The following code merely exists to slow down the TS compilation
// of this file (it slows it down a lot, several seconds on my machine!).
// To trigger a recompilation change the number suffix (it looks like
// simply re-saving / touching the file isn't sufficient to cause a
// recompilation).

export function complexGeneric1234(name: keyof JSX.IntrinsicElements) {
  const wrapped: React.FC<JSX.IntrinsicElements[typeof name]> = (props) =>
    React.createElement(name, { ...props }, props.children);
  return wrapped;
}
```

**`worker/index.ts`**:

```ts
// eslint-disable-next-line
import Worker from "comlink-loader!./worker";

export default Worker;
```

**`worker/worker.ts`**:

```ts
export function compute(): number {
  return 42;
}
```


**`worker/custom.d.ts`**:

```ts
declare module "comlink-loader!*" {
  class WebpackWorker extends Worker {
    constructor();
    compute(): Promise<number>;
  }
  export = WebpackWorker;
}
```


### Expected behavior

Compilation not stuck.

### Actual behavior

Compilation is randomly stuck at [WebpackDevServerUtils.js#L187](https://github.com/facebook/create-react-app/blob/fddce8a9e21bf68f37054586deb0c8636a45f50b/packages/react-dev-utils/WebpackDevServerUtils.js#L187)

```
Files successfully emitted, waiting for typecheck results...
```

because the promise in [WebpackDevServerUtils.js#L192](https://github.com/facebook/create-react-app/blob/fddce8a9e21bf68f37054586deb0c8636a45f50b/packages/react-dev-utils/WebpackDevServerUtils.js#L192) never completes.

The cause of the problem seems to be a race condition between the callbacks `"beforeCompile"` ([WebpackDevServerUtils.js#L143-L147](https://github.com/facebook/create-react-app/blob/fddce8a9e21bf68f37054586deb0c8636a45f50b/packages/react-dev-utils/WebpackDevServerUtils.js#L143-L147)) and `"afterTypeScriptCheck"` ([WebpackDevServerUtils.js#L149-L163](https://github.com/facebook/create-react-app/blob/fddce8a9e21bf68f37054586deb0c8636a45f50b/packages/react-dev-utils/WebpackDevServerUtils.js#L149-L163)). Due to the web worker loader, there are multiple calls to these callbacks, in contrast to just a single call in normal projects.

I have placed `console.log`'s in the callbacks, and enforcing a non-interactive execution via `npm start | cat` reveals this order in case the compilation gets stuck:

```
Compiling...
beforeCompile
afterTypeScriptCheck
beforeCompile
Files successfully emitted, waiting for typecheck results...
```

Note that `beforeCompile` gets called a second time after the promise has been completed. This resets the promise to "pending" state, and therefore the compilation gets stuck.

For comparison, this is an execution which did not trigger the problem:

```
Compiling...
beforeCompile
beforeCompile
Files successfully emitted, waiting for typecheck results...
afterTypeScriptCheck
Compiled with warnings.
```

Note that this time the second `beforeCompile` occurred early enough to not reset the promise. Therefore it looks like it is a race condition between these callbacks.

### Reproducible demo

https://github.com/bluenote10/js-debug-projects/tree/master/cra_webworker