mirror of https://github.com/vuejs/vitepress
parent
b5e25f949c
commit
d16e67a02a
@ -1,96 +0,0 @@
|
|||||||
/* ---------------------------------------------------------
|
|
||||||
* Copyright (c) 2023 Yuxuan Zhang, web-dev@z-yx.cc
|
|
||||||
* This source code is licensed under the MIT license.
|
|
||||||
* You may find the full license in project root directory.
|
|
||||||
* ------------------------------------------------------ */
|
|
||||||
|
|
||||||
import os from 'os'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a callback pool, with a maximum number of parallel tasks.
|
|
||||||
* Queued tasks will be executed directly when the pool is not full.
|
|
||||||
* Otherwise, they will be executed in a first-in-first-out manner
|
|
||||||
* when a dispatched task finishes.
|
|
||||||
* ---
|
|
||||||
* When used with 'node:child_process', you can control the pooling
|
|
||||||
* of parallel tasks, and avoid spawning too many child processes.
|
|
||||||
*/
|
|
||||||
export default class Pool extends EventTarget {
|
|
||||||
// Number of dispatched tasks (currently in progress)
|
|
||||||
private numExecutors: number = 0
|
|
||||||
|
|
||||||
// The pending task queue. Promise return type <T> varies by task
|
|
||||||
private pendingTaskQueue: Array<() => Promise<any>> = []
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This executor will keep executing next available task in queue.
|
|
||||||
* Therefore, it always owns a running task until the queue drains.
|
|
||||||
* You can think of it as a "worker thread" in a thread pool.
|
|
||||||
*/
|
|
||||||
private executor() {
|
|
||||||
// Only launch new executor when the pool is not full
|
|
||||||
if (this.numExecutors >= this.maxThreads) return
|
|
||||||
// Register new executor
|
|
||||||
this.numExecutors++
|
|
||||||
;(async () => {
|
|
||||||
while (this.pendingTaskQueue.length > 0) {
|
|
||||||
try {
|
|
||||||
const nextTaskInQueue = this.pendingTaskQueue.shift()!
|
|
||||||
await nextTaskInQueue()
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
// Unregister the executor
|
|
||||||
this.numExecutors--
|
|
||||||
// Dispatch "drain" event when the last executor finishes
|
|
||||||
if (this.numExecutors === 0) this.dispatchEvent(new Event('drain'))
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param maxThreads Maximum number of parallel tasks
|
|
||||||
*/
|
|
||||||
constructor(private maxThreads: number = os.cpus().length) {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatch a task to the pool. Use it like `new Promise<T>(task)`
|
|
||||||
* @param task The task call back, just like the one in Promise constructor
|
|
||||||
* @returns
|
|
||||||
* The constructed promise object, will resolve to the value
|
|
||||||
* passed to resolve callback
|
|
||||||
*/
|
|
||||||
add<T = any>(task: (...args: any[]) => T, ...args: any[]): Promise<T> {
|
|
||||||
// Create deferred promise handlers
|
|
||||||
let resolve: (v: T) => void, reject: (reason?: any) => void
|
|
||||||
const promise = new Promise<T>(
|
|
||||||
(...callbacks) => ([resolve, reject] = callbacks)
|
|
||||||
)
|
|
||||||
// Push the task to queue for deferred resolution
|
|
||||||
// Notice the task may NOT be invoked right away
|
|
||||||
this.pendingTaskQueue.push(async () => {
|
|
||||||
try {
|
|
||||||
resolve(await task(...args))
|
|
||||||
} catch (error: any) {
|
|
||||||
reject(error)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// Try to launch a new executor
|
|
||||||
this.executor()
|
|
||||||
// Return the promise
|
|
||||||
return promise
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for all dispatched executors to finish.
|
|
||||||
* Return immediately if no executor is running.
|
|
||||||
* @returns A promise that resolves when the pool drains
|
|
||||||
*/
|
|
||||||
async drain() {
|
|
||||||
if (this.numExecutors > 0)
|
|
||||||
await new Promise<void>((resolve) =>
|
|
||||||
this.addEventListener('drain', () => resolve(), { once: true })
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in new issue