diff --git a/promises/index.js b/promises/index.js new file mode 100644 index 0000000..f6dfeac --- /dev/null +++ b/promises/index.js @@ -0,0 +1,66 @@ +const path = require("path"); +const { Worker } = require("worker_threads"); + +let workerPromise; +async function initWorker() { + if (workerPromise) return workerPromise; + const worker = new Worker(path.join(__dirname, "worker.js")); + workerPromise = new Promise((resolve) => { + function onReady() { + worker.off("message", onReady); + let pendingPromises = {}; + worker.on("message", ({ id, res, err }) => { + if (err) pendingPromises[id].reject(err); + else pendingPromises[id].resolve(res); + delete pendingPromises[id]; + }); + const wrappedWorker = { + sendMessage: ({ isClass, self, method, args }) => { + const id = Math.random().toString(36).substring(2); + return new Promise((resolve, reject) => { + pendingPromises[id] = { resolve, reject }; + worker.postMessage({ id, isClass, self, method, args }); + }); + }, + }; + resolve(wrappedWorker); + } + worker.on("message", onReady); + }); + + return workerPromise; +} + +function proxify(worker, res) { + return new Proxy(res, { + get(obj, method) { + if (method === "then" || method === "catch" || method === "finally") + return obj[method]; + return (...args) => worker.sendMessage({ self: obj, method, args }); + }, + }); +} + +function wrapClass(className) { + return async (...args) => { + const worker = await initWorker(); + const res = await worker.sendMessage({ + isClass: true, + method: className, + args, + }); + return proxify(worker, res); + }; +} + +function wrapMethod(methodName) { + return async (...args) => { + const worker = await initWorker(); + return worker.sendMessage({ method: methodName, args }); + }; +} + +exports.Wallet = wrapClass("Wallet"); +exports.dropOnline = wrapMethod("dropOnline"); +exports.generateKeys = wrapMethod("generateKeys"); +exports.restoreKeys = wrapMethod("restoreKeys"); diff --git a/promises/worker.js b/promises/worker.js new file mode 100644 index 0000000..db5378f --- /dev/null +++ b/promises/worker.js @@ -0,0 +1,36 @@ +const { parentPort } = require("worker_threads"); +const wrapper = require("../wrapper"); + +const registry = {}; +parentPort.on("message", ({ id, isClass, self, method, args }) => { + try { + if (self) self = getArgValue(self); + else self = wrapper; + if (typeof self[method] !== "function") { + throw new Error( + `${self[method]} is not a function (calling ${method})`, + ); + } + + let res; + if (isClass) res = new self[method](...args.map(getArgValue)); + else res = self[method](...args.map(getArgValue)); + try { + parentPort.postMessage({ id, res }); + } catch (e) { + if (e.name === "DataCloneError") { + registry[id] = res; + parentPort.postMessage({ id, res: { _id: id } }); + } else { + throw e; + } + } + } catch (err) { + parentPort.postMessage({ id, err }); + } +}); +parentPort.postMessage(null); + +function getArgValue(a) { + return registry[a?._id] || a; +}