Skip to content

Commit b42ec93

Browse files
committed
Implement promises library with non-blocking methods
1 parent 9ec7913 commit b42ec93

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

promises/index.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
const path = require("path");
2+
const { Worker } = require("worker_threads");
3+
4+
let workerPromise;
5+
async function initWorker() {
6+
if (workerPromise) return workerPromise;
7+
const worker = new Worker(path.join(__dirname, "worker.js"));
8+
workerPromise = new Promise((resolve) => {
9+
function onReady() {
10+
worker.off("message", onReady);
11+
let pendingPromises = {};
12+
worker.on("message", ({ id, res, err }) => {
13+
if (err) pendingPromises[id].reject(err);
14+
else pendingPromises[id].resolve(res);
15+
delete pendingPromises[id];
16+
});
17+
const wrappedWorker = {
18+
sendMessage: ({ isClass, self, method, args }) => {
19+
const id = Math.random().toString(36).substring(2);
20+
return new Promise((resolve, reject) => {
21+
pendingPromises[id] = { resolve, reject };
22+
worker.postMessage({ id, isClass, self, method, args });
23+
});
24+
},
25+
};
26+
resolve(wrappedWorker);
27+
}
28+
worker.on("message", onReady);
29+
});
30+
31+
return workerPromise;
32+
}
33+
34+
function proxify(worker, res) {
35+
return new Proxy(res, {
36+
get(obj, method) {
37+
if (method === "then" || method === "catch" || method === "finally")
38+
return obj[method];
39+
return (...args) => worker.sendMessage({ self: obj, method, args });
40+
},
41+
});
42+
}
43+
44+
function wrapClass(className) {
45+
return async (...args) => {
46+
const worker = await initWorker();
47+
const res = await worker.sendMessage({
48+
isClass: true,
49+
method: className,
50+
args,
51+
});
52+
return proxify(worker, res);
53+
};
54+
}
55+
56+
function wrapMethod(methodName) {
57+
return async (...args) => {
58+
const worker = await initWorker();
59+
return worker.sendMessage({ method: methodName, args });
60+
};
61+
}
62+
63+
exports.Wallet = wrapClass("Wallet");
64+
exports.dropOnline = wrapMethod("dropOnline");
65+
exports.generateKeys = wrapMethod("generateKeys");
66+
exports.restoreKeys = wrapMethod("restoreKeys");

promises/worker.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const { parentPort } = require("worker_threads");
2+
const wrapper = require("../wrapper");
3+
4+
const registry = {};
5+
parentPort.on("message", ({ id, isClass, self, method, args }) => {
6+
try {
7+
if (self) self = getArgValue(self);
8+
else self = wrapper;
9+
if (typeof self[method] !== "function") {
10+
throw new Error(
11+
`${self[method]} is not a function (calling ${method})`,
12+
);
13+
}
14+
15+
let res;
16+
if (isClass) res = new self[method](...args.map(getArgValue));
17+
else res = self[method](...args.map(getArgValue));
18+
try {
19+
parentPort.postMessage({ id, res });
20+
} catch (e) {
21+
if (e.name === "DataCloneError") {
22+
registry[id] = res;
23+
parentPort.postMessage({ id, res: { _id: id } });
24+
} else {
25+
throw e;
26+
}
27+
}
28+
} catch (err) {
29+
parentPort.postMessage({ id, err });
30+
}
31+
});
32+
parentPort.postMessage(null);
33+
34+
function getArgValue(a) {
35+
return registry[a?._id] || a;
36+
}

0 commit comments

Comments
 (0)