import { computeStaggerMs, humanPause } from "@/lib/threads-browser/human-behavior"; /** 有限平行執行,批次間加隨機間隔以降低被封風險 */ export async function runWithConcurrency( items: T[], worker: (item: T, index: number) => Promise, options?: { concurrency?: number; staggerMs?: [number, number]; onProgress?: (done: number, total: number) => void; shouldAbort?: () => boolean | Promise; } ): Promise { const concurrency = Math.max(1, options?.concurrency ?? 1); const stagger = options?.staggerMs ?? [3500, 9000]; const results: R[] = new Array(items.length); let nextIndex = 0; let done = 0; async function runWorker() { while (true) { if (await options?.shouldAbort?.()) { throw new Error("__JOB_ABORT__"); } const index = nextIndex++; if (index >= items.length) return; if (index > 0) { await new Promise((resolve) => setTimeout(resolve, computeStaggerMs(stagger))); if (Math.random() < 0.15 + Math.random() * 0.18) { await humanPause(Math.random() < 0.55 ? "short" : "medium"); } } if (await options?.shouldAbort?.()) { throw new Error("__JOB_ABORT__"); } results[index] = await worker(items[index], index); done++; options?.onProgress?.(done, items.length); } } const workers = Array.from({ length: Math.min(concurrency, items.length) }, () => runWorker() ); await Promise.all(workers); return results; }