UNPKG

process-worker-executor

Version:

将多进程和多线程封装为统一的 Async/Await 调用方式, 简化 IPC 消息通信的复杂性

120 lines (117 loc) 3.73 kB
const log = require('../utils/logger') const _ = require('lodash') class ExecutorClient { logger logPath logFile #id = 1 #cache = new Map() disposable = [] constructor(opts) { this.logPath = opts.logPath this.logFile = opts.logFile if (this.logPath && this.logFile) { const name = this.logFile + '-client.txt' this.logger = log.create(this.logPath, name) this.disposable.push(() => { log.close(name) }) } } #decodeArgs(args) { return args.map(it => { if (it.type == 'FUNCTION') { return (...params) => { this.#send(it.id, 'ARGS_FUNCTION', params) } } return it }) } #encodeReturn(response) { if (_.isFunction(response)) { return { id, type: 'FUNCTION' } } if (_.isPlainObject(response)) { return _.mapValues(response, value => { if (_.isFunction(value)) { const id = this.#id++ this.#cache.set(id, value.bind(response)) return { id, type: 'FUNCTION' } } return value }) } return response } #createMessage(id, command, data) { const message = { id, command, data } return message } async #send(id, command, data) { const msg = this.#createMessage(id, command, data) this.onSend(msg) } async on(message) { const { id, command, data, metedata } = message let result const args = this.#decodeArgs(data) try { switch (command) { case 'RETURN_FUNCTION': const [fnId, ...params] = args const fn = this.#cache.get(fnId) result = await fn?.(...params) break case 'CLOSE': await this.logger?.info('执行器已经关闭, 资源已经释放...') for (const dispose of this.disposable) { await dispose() } break default: result = await this.onExec(metedata, command, ...args) break } const response = this.#encodeReturn(result) this.#send(id, 'FINISH', response) } catch (error) { this.#send(id, 'ERROR', { message: error.message, stack: error.stack }) } } async onExec(metedata, command, ...args) { this.logger?.info({ command, args, metedata }) const module = require(metedata.scriptPath) if (_.isFunction(module)) { const ctx = { ...global, _metedata: metedata } if (command) { return await module.call(ctx, command, ...args) } return await module.call(ctx, ...args) } if (_.isPlainObject(module)) { module._metedata = metedata return await module[command]?.(...args) } throw new Error('脚本导出的不是方法也不是普通对象, 请检查脚本: ' + metedata.scriptPath) } async onInit() {} async onSend() {} } module.exports = ExecutorClient