@wfh/thread-promise-pool
Version:
2 Node.js utilities: a thread worker pool and a promise queue
122 lines (106 loc) • 3.21 kB
text/typescript
// tslint:disable no-console
let verbose = false;
function sendMsg(msg: any) {
return process.send!(msg, null, {}, err => {
if (err)
console.error(`[thread-pool] pid:${process.pid} failed to send Error message: `, msg, err);
});
}
process.on('uncaughtException', onUncaughtException);
// let doNotSendToParent = false;
function onUncaughtException(err: any) {
// log.error('Uncaught exception', err, err.stack);
console.error(`[thread-pool] pid:${process.pid} Uncaught exception: `, err);
sendMsg({
type: 'error',
data: err.toString()
});
}
process.on('unhandledRejection', onUnhandledRejection);
function onUnhandledRejection(err: any) {
console.error(`[thread-pool] pid:${process.pid} unhandledRejection`, err);
sendMsg({
type: 'error',
data: err ? err.toString() : err
});
}
export interface InitialOptions {
verbose?: boolean;
/** After worker being created, the exported function will be run,
* You can put any initial logic in it, like calling `require('source-map-support/register')` or
* setup process event handling for uncaughtException and unhandledRejection.
*/
initializer?: {file: string; exportFn?: string};
}
export interface Task {
file: string;
/**
* A function which can return Promise or non-Promise value
*/
exportFn?: string;
args?: any[];
}
export interface Command {
exit: boolean;
}
if (process.send) {
process.on('message', executeOnEvent);
}
async function executeOnEvent(data: Task | Command) {
if ((data as Command).exit) {
if (verbose)
console.log(`[thread-pool] child process ${process.pid} exit`);
process.off('message', executeOnEvent);
// process.off('uncaughtException', onUncaughtException);
// process.off('unhandledRejection', onUnhandledRejection);
// setImmediate(() => process.exit(0));
return;
}
if ((data as InitialOptions).verbose != null) {
verbose = !!(data as InitialOptions).verbose;
}
try {
let result: any;
const initData = data as InitialOptions;
if (initData.initializer) {
if (verbose) {
console.log(`[thread-pool] child process ${process.pid} init`);
}
const exportFn = initData.initializer.exportFn;
if (exportFn) {
await Promise.resolve(require(initData.initializer.file)[exportFn]());
} else {
require(initData.initializer.file);
}
} else {
if (verbose) {
console.log(`[thread-pool] child process ${process.pid} run`);
}
const exportFn = (data as Task).exportFn;
if (exportFn) {
result = await Promise.resolve(require((data as Task).file)[exportFn](
...((data as Task).args || [])
));
} else {
require((data as Task).file);
}
}
if (verbose) {
console.log(`[thread-pool] child process ${process.pid} wait`);
}
sendMsg({ type: 'wait', data: result });
} catch (ex) {
console.log(`[thread-pool] child process ${process.pid} error`, ex);
try {
sendMsg({
type: 'error',
data: ex.toString()
});
} catch (err) {
sendMsg({
type: 'error',
data: err.toString()
});
}
}
}