thread-tasks
Version:
Thread Tasks: Offload your CPU-intensive tasks to separate threads.
185 lines (181 loc) • 5.99 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// src/thread-tasks.ts
import { Worker } from "worker_threads";
import { tmpdir } from "os";
import { join } from "path";
import { transpile, ScriptTarget } from "typescript";
var workersDirectory = join(tmpdir(), "parallel-workers");
function transpileWorkerFile(wokerCode) {
return transpile(wokerCode, {
allowJs: true,
target: ScriptTarget.ES2015
});
}
function createWorkerFile(task, n) {
return __async(this, null, function* () {
const fs = yield import("fs");
const taskStr = task.fn.toString();
if (!fs.existsSync(workersDirectory)) {
fs.mkdirSync(workersDirectory, { recursive: true });
}
const workerStr = `
const { workerData, parentPort } = require('worker_threads');
const runTask = (data) => {
return new Promise(async function (resolve, reject) {
try {
const fn = ${taskStr};
const result = await fn({ ...data });
resolve(result);
} catch (error) {
reject(error);
}
});
};
runTask(workerData.data)
.then(result => parentPort && parentPort.postMessage(result))
.catch(err => {
throw err;
});
`;
const workerCode = transpileWorkerFile(workerStr);
fs.writeFileSync(`${workersDirectory}/worker${n}.js`, workerCode);
});
}
function deleteWorkerFile(n) {
return __async(this, null, function* () {
const filePath = `${workersDirectory}/worker${n}.js`;
const fs = yield import("fs");
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
});
}
function threadTasks(tasks) {
return tasks.map((task) => __async(this, null, function* () {
const randomIdWithTimestamp = Date.now() + Math.floor(Math.random() * 1e3);
yield createWorkerFile(task, randomIdWithTimestamp);
const worker = new Worker(
join(workersDirectory, `worker${randomIdWithTimestamp}.js`),
{
workerData: {
data: task.args ? __spreadValues({}, task.args) : {}
}
}
);
worker.on("message", (msg) => __async(this, null, function* () {
task.onSuccess && (yield task.onSuccess(msg));
worker.terminate();
deleteWorkerFile(randomIdWithTimestamp);
}));
worker.on("error", (err) => __async(this, null, function* () {
task.onError && (yield task.onError(err));
worker.terminate();
deleteWorkerFile(randomIdWithTimestamp);
}));
return worker;
}));
}
function threadTasksAdvanced({
tasks,
getMaxThreads,
afterAll
}) {
return new Promise((resolve, reject) => {
let counter = 0;
let activeWorkers = 0;
let maxCpus = null;
let getThreadCount = getMaxThreads != null ? getMaxThreads : () => {
if (maxCpus === null) {
const os = __require("os");
const cpuCount = os.cpus().length;
maxCpus = cpuCount;
return cpuCount;
}
return maxCpus;
};
let threadCount = getThreadCount();
const interval = setInterval(() => __async(this, null, function* () {
const maxThreadCount = getThreadCount();
if (threadCount !== maxThreadCount) {
}
threadCount = maxThreadCount;
if (activeWorkers < maxThreadCount && counter < tasks.length) {
const task = tasks[counter];
const randomIdWithTimestamp = Date.now() + Math.floor(Math.random() * 1e3);
yield createWorkerFile(task, randomIdWithTimestamp);
const worker = new Worker(
join(workersDirectory, `worker${randomIdWithTimestamp}.js`),
{
workerData: {
data: task.args ? __spreadValues({}, task.args) : {}
}
}
);
counter++;
activeWorkers++;
worker.on("message", (msg) => __async(this, null, function* () {
task.onSuccess && (yield task.onSuccess(msg));
worker.terminate();
activeWorkers--;
deleteWorkerFile(randomIdWithTimestamp);
}));
worker.on("error", (err) => __async(this, null, function* () {
task.onError && (yield task.onError(err));
worker.terminate();
activeWorkers--;
deleteWorkerFile(randomIdWithTimestamp);
}));
}
if (counter === tasks.length && activeWorkers === 0) {
afterAll && (yield afterAll());
clearInterval(interval);
resolve(true);
}
}), 1);
});
}
export {
threadTasks,
threadTasksAdvanced
};