Repository: babel/babel
File: scripts/parallel-tsc/tsc.js (JavaScript)

Code before:
    const tasks = [this.#pending[0]];
    let weight = this.#getWeight(tasks[0]);
    const toRemove = new Set([0]);
    for (let i = 1; i < this.#pending.length && weight < this.#maxWeight; i++) {
      const task = this.#pending[i];
      const taskWeight = this.#getWeight(task);
      if (weight + taskWeight <= this.#maxWeight) {
        weight += taskWeight;
        tasks.push(task);
        toRemove.add(i);
      }
    }
    this.#pending = this.#pending.filter((_, i) => !toRemove.has(i));

    const taskProcess = cp.fork(this.#filename, ["-b", ...tasks, "--pretty"], {
      stdio: this.#showOutput(tasks) ? "inherit" : "ignore",
    });

    taskProcess.once("exit", async code => {
      const startTime = this.#running.get(taskProcess);
      const duration = performance.now() - startTime;
      await this.#onTaskDone(tasks, code, duration);
      this.#running.delete(taskProcess);

      // Spin the event loop so that #run can pick up the next task
      await 0;

      this.#runTasks();

      if (this.#running.size === 0 && this.#pending.length === 0) {
        this.#emptyListeners.forEach(f => f());
      }
    });

    this.#onTaskStart(tasks, weight);
    this.#running.set(taskProcess, performance.now());
  }

  #runTasks() {
    while (this.#running.size < this.#concurrency && this.#pending.length > 0) {
      this.#runNextTask();
    }
  }

  get waitForEmpty() {
    return new Promise(resolve => {
      this.#emptyListeners.push(resolve);
    });
  }
}


Review comment (suggestion):
This doesn't actually spin the event loop. `setTimeout` has lower priority than `queueMicrotask`, while `promise.then` for an already-resolved promise has the same priority.

Code after:

    const tasks = [this.#pending[0]];
    let weight = this.#getWeight(tasks[0]);
    const toRemove = new Set([0]);
    for (let i = 1; i < this.#pending.length && weight < this.#maxWeight; i++) {
      const task = this.#pending[i];
      const taskWeight = this.#getWeight(task);
      if (weight + taskWeight <= this.#maxWeight) {
        weight += taskWeight;
        tasks.push(task);
        toRemove.add(i);
      }
    }
    this.#pending = this.#pending.filter((_, i) => !toRemove.has(i));

    const taskProcess = cp.fork(this.#filename, ["-b", ...tasks, "--pretty"], {
      stdio: this.#showOutput(tasks) ? "inherit" : "ignore",
    });

    taskProcess.once("exit", async code => {
      const startTime = this.#running.get(taskProcess);
      const duration = performance.now() - startTime;
      await this.#onTaskDone(tasks, code, duration);
      this.#running.delete(taskProcess);

      // Spin the event loop so that #run can pick up the next task
      await setTimeout(0);

      this.#runTasks();

      if (this.#running.size === 0 && this.#pending.length === 0) {
        this.#emptyListeners.forEach(f => f());
      }
    });

    this.#onTaskStart(tasks, weight);
    this.#running.set(taskProcess, performance.now());
  }

  #runTasks() {
    while (this.#running.size < this.#concurrency && this.#pending.length > 0) {
      this.#runNextTask();
    }
  }

  get waitForEmpty() {
    return new Promise(resolve => {
      this.#emptyListeners.push(resolve);
    });
  }
}