Skip to content

Commit

Permalink
added support for client functions, callbacks in callbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
angrymouse committed Jun 17, 2021
1 parent 707cd2e commit 5172671
Show file tree
Hide file tree
Showing 4 changed files with 263 additions and 47 deletions.
39 changes: 23 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,21 @@ yarn add remote-functions
let FManager = require("remote-functions");
let rf = new FManager();
let WebSocket = require("ws");
let wss = WebSocket.Server({ port: 8080 });
let wss = new WebSocket.Server({ port: 8080 });

wss.on("connection", (socket) => {
let { handleMessage, disconnected } = rf.connectClient(data=>socket.send(data));
socket.on("close", disconnected);
socket.on("message", handleMessage);
wss.on("connection", async (socket) => {
let client = await rf.connectClient((d) => socket.send(d));

socket.on("close", client.disconnected);
socket.on("message", (m) => client.handleMessage(m));
await client.clientReady();
});

let crypto = require("crypto");
//module, that works only in node.js environment (database or etc, we used crypto only for example)
function getUUID(prefix, cb) {
setInterval(() => {
cb("hello!"); // we can even work with callbacks!
cb("hello!"); // we can even pass callbacks!
}, 1000);
// prefix used to show that we can pass any arguments to functions
console.log(prefix);
Expand All @@ -50,6 +52,7 @@ function getUUID(prefix, cb) {
rf.addFunc(getUUID);
// we need to register our function to use it


```

### On client side
Expand All @@ -76,18 +79,22 @@ rf.addFunc(getUUID);
// All functions in RF.js is asynchronous by the nature, so we wrapped our code in async function to use awaits

let ws = new WebSocket("ws://localhost:8080");
let rf = new RF((data) => ws.send(data));
window.rf = new RF();
ws.onmessage = (message) => rf.RFMessageReceived(message.data);
await rf.prepare();

let uuidFromServer = await rf.getUUID("myprefix", (text) => {
console.log(text); //we can work with callbacks!
});
//MAGIC! Calling function on server!

console.log(uuidFromServer);
// myprefix-1234-5678-9012-3456
ws.onopen = async () => {
//we can't send data to socket while it's connecting, so we need to wait while socket will be connected
await rf.RFPrepare((data) => ws.send(data));

let uuidFromServer = await rf.getUUID("myprefix", (text) => {
console.log(text); //we can work with callbacks!
});
//MAGIC! Calling function on server!

console.log(uuidFromServer);
// myprefix-1234-5678-9012-3456
};
})();



```
2 changes: 1 addition & 1 deletion bundle/rf.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

148 changes: 130 additions & 18 deletions client/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
class RFManager {
__functions = new Map();
__callbacks = new Map();
__sendQueue = [];
__awaiting = {};
constructor(sendMessage) {
this.__sendRaw = sendMessage;
this.__ready = false;
constructor(functions = []) {
this.__sendRaw = (data) => this.__sendQueue.push(data);
this.__readyState = 0;
this.__functions = new Map(
functions.map((f) => [
f.name,
async (...args) => await this.__callFunc(f, args),
])
);
}
prepare() {
RFPrepare(sendMessage) {
this.__sendRaw = sendMessage;
this.__sendQueue.forEach((message) => this.__sendRaw(message));
this.__sendQueue = null;
return new Promise((resolve, reject) => {
if (this.__ready) {
if (this.__readyState == 2) {
resolve();
} else {
this.__send(
"availableFuncs",
Array.from(this.__functions.keys())
);

this.__readyIndicator = { resolve, reject };
}
});
Expand All @@ -19,14 +34,16 @@ class RFManager {

this.__dispatch(message.type, message.data);
}

__dispatch(type, data) {
({
availableFuncs: () => {
data.forEach((name) => {
this[name] = (...args) => this.__callFunc(name, args);
this[name] = (...args) => this.__callServerFunc(name, args);
});
this.__ready = true;
if (this.__readyIndicator) {
this.__send("funcsReceived", true);
this.__readyState++;
if (this.__readyState == 2) {
this.__readyIndicator.resolve();
delete this.__readyIndicator;
}
Expand All @@ -42,11 +59,49 @@ class RFManager {
error: () => {
console.error(data);
},
callClientFunc: () => {
callClientCallback: () => {
let { id, args } = data;
let func = this.__functions.get(id);
let func = this.__callbacks.get(id);
if (func) {
func(...args);
func(...this.__deserializeArgs(args));
}
},
callClientFunc: () => {
let fun = this.__functions.get(data.fname);
if (!fun) {
return;
}
fun(...this.__deserializeArgs(data.args))
.then((res) => {
this.__send(
JSON.stringify({
type: "functionResult",
data: {
reqId: data.reqId,
resultType: "resolve",
result: res,
},
})
);
})
.catch((error) => {
this.__send(
JSON.stringify({
type: "functionResult",
data: {
reqId: data.reqId,
resultType: "reject",
result: error.toString(),
},
})
);
});
},
funcsReceived: () => {
this.__readyState++;
if (this.__readyState == 2) {
this.__readyIndicator.resolve();
delete this.__readyIndicator;
}
},
}[type]());
Expand All @@ -60,8 +115,42 @@ class RFManager {
})
);
}

__callFunc(fname, args) {
__deserializeArgs(args) {
return args.map((a) => this.__deserializeArg(a));
}
__deserializeArg(arg) {
if (typeof arg == "object") {
return (
{
object: () => this.__deserializeObject(arg.data),
function:
() =>
async (...args) => {
this.__callServerCallback(arg.data, args);
},
}[arg.type]() || arg
);
} else {
return arg;
}
}
__deserializeObject(object) {
return Object.entries(object)
.map((_key, value) => this.__deserializeArg(value))
.reduce((pv, cv) => ({ ...pv, [cv[0]]: cv[1] }), {});
}
__callServerCallback(id, args) {
this.__send(
JSON.stringify({
type: "callServerCallback",
data: {
id,
args: this.__serializeArgs(args),
},
})
);
}
__callServerFunc(fname, args) {
return new Promise((resolve, reject) => {
let reqId = Math.random().toString(16).substr(2);
this.__send("callFunc", {
Expand All @@ -75,6 +164,7 @@ class RFManager {
};
});
}

__serializeArgs(args) {
return args.map(this.__serializeArg);
}
Expand All @@ -89,7 +179,7 @@ class RFManager {
function: function () {
return {
type: "function",
data: _this.__registerFunction(arg),
data: _this.__registerCallback(arg),
};
},
}[typeof arg];
Expand All @@ -100,8 +190,8 @@ class RFManager {
.map((_key, value) => this.__serializeArg(value))
.reduce((pv, cv) => ({ ...pv, [cv[0]]: cv[1] }), {});
}
__registerFunction(func) {
let foundFunction = Array.from(this.__functions.entries()).find(
__registerCallback(func) {
let foundFunction = Array.from(this.__callbacks.entries()).find(
(funcInfo) => {
let [, fun] = funcInfo;
return func == fun;
Expand All @@ -112,9 +202,31 @@ class RFManager {
[id] = foundFunction;
} else {
id = Math.random().toString(16).substr(2);
this.__functions.set(id, func);
this.__callbacks.set(id, func);
}
return id;
}
RFAddFunc(func) {
this.__functions.set(
func.name,
async (...args) => await this.__callFunc(func, args)
);

this.__send(
JSON.stringify({
type: "availableFuncs",
data: Array.from(this.functions.keys()),
})
);
}
async __callFunc(func, args) {
return new Promise(async (resolve, reject) => {
try {
resolve(await func(...args));
} catch (e) {
reject(e);
}
});
}
}
export default RFManager;
Loading

0 comments on commit 5172671

Please sign in to comment.