Skip to content

Commit

Permalink
add proxy callback
Browse files Browse the repository at this point in the history
  • Loading branch information
lyswhut committed Jan 5, 2024
1 parent 0d7fc69 commit 6eef3bc
Show file tree
Hide file tree
Showing 9 changed files with 464 additions and 67 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Change log format is based on [Keep a Changelog](http://keepachangelog.com/).

### Change

- Add `createProxyCallback`
- Add `releaseAllProxyCallback`
- Add `createRemoteGroup`
- Add `isSendErrorStack` option
- Remove `createQueueRemote`
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,11 @@ onmessage = (event) => {
## Options

```ts
type ReadObj = Record<string, ((...args: any[]) => any) | string | number | object>

interface Options {
/**
* required proxy object
*/
funcsObj: Readonly<ReadObj>
funcsObj: Readonly<Record<string, ((...args: any[]) => any) | string | number | object>>
/**
* send message function
*/
Expand Down Expand Up @@ -169,11 +167,19 @@ type createMsg2call = <T>(options: Options) => {
*/
destroy: () => void;
}
/**
* create a proxy callback
*/
type createProxyCallback = <T extends Function>(callback: T) => T & { releaseProxy: () => void };
/**
* release all created proxy callback
*/
type releaseAllProxyCallback = () => void;
```

<!-- ## CHANGELOG
## CHANGELOG

See [CHANGELOG.md](https://github.com/lyswhut/message2call/blob/master/CHANGELOG.md) -->
See [CHANGELOG.md](https://github.com/lyswhut/message2call/blob/master/CHANGELOG.md)

## LICENSE

Expand Down
13 changes: 11 additions & 2 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
* on call error hook
*/
onError: function(err, path, groupName) {
console.log('error:', err, path, groupName)
// console.log('error:', err, path, groupName)
},
/**
* call timeout
Expand All @@ -54,7 +54,16 @@
const result = await remote.count(6)
console.log('[index]', result)

remote.callFailed().catch(err => console.log('[index]', err))
await remote.callFailed().catch(err => console.log('[index]', err))

const onProgress = Message2call.createProxyCallback((progress) => {
console.log('[index]', 'download progress: ' + progress + '%')
if (progress == 100) {
onProgress.releaseProxy()
return 'end'
} else return 'progress'
})
remote.downloadFile('test.txt', onProgress)
})()
</script>

Expand Down
15 changes: 15 additions & 0 deletions demo/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ const exposeObj = {
}, 2000);
})
},
async downloadFile(title, callback) {
console.log('start download', title)
let total = 0
const timeout = setInterval(() => {
total += (Math.floor(Math.random() * 2000) + 500) / 100
total = Number(total.toFixed(2))
if (total >= 100) {
total = 100
clearInterval(timeout)
}
callback(total).then((message) => {
console.log('[worker]', 'callback result: ' + message)
})
}, 500)
},
}
const message2call = Message2call.createMsg2call({
/**
Expand Down
14 changes: 13 additions & 1 deletion dist/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import type { Options } from '../types/common';
interface ProxyCallback {
__msg2call_cbname__: string;
releaseProxy: () => void;
}
interface RemoteGroup {
handling: boolean;
options: {
Expand All @@ -19,10 +23,18 @@ export declare const createMsg2call: <T>(options: Options) => {
/**
* on message function
*/
message: ({ name, path, error, data }: any) => void;
message: (message: any) => void;
/**
* destroy
*/
destroy: () => void;
};
/**
* create a proxy callback
*/
export declare const createProxyCallback: <T extends Function>(callback: T) => T & Pick<ProxyCallback, "releaseProxy">;
/**
* release all created proxy callback
*/
export declare const releaseAllProxyCallback: () => void;
export type * from '../types/common';
157 changes: 136 additions & 21 deletions dist/message2call.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ const nextTick = typeof setImmediate == 'function'
? queueMicrotask
: setTimeout;

var CALL_TYPES;
(function (CALL_TYPES) {
CALL_TYPES[CALL_TYPES["REQUEST"] = 0] = "REQUEST";
CALL_TYPES[CALL_TYPES["RESPONSE"] = 1] = "RESPONSE";
CALL_TYPES[CALL_TYPES["CALLBACK_REQUEST"] = 2] = "CALLBACK_REQUEST";
CALL_TYPES[CALL_TYPES["CALLBACK_RESPONSE"] = 3] = "CALLBACK_RESPONSE";
})(CALL_TYPES || (CALL_TYPES = {}));
const proxyCallbacks = new Map();
const emptyObj = {};
const noop = () => { };
const funcsTools = {
Expand All @@ -30,13 +38,71 @@ const funcsTools = {
this.onCallBeforeParams = options.onCallBeforeParams;
return this.createProxy(this, null);
},
async handleResponseData(eventName, path, args) {
async handleCallbackRequest(callbackName, args) {
const handler = proxyCallbacks.get(callbackName);
if (!handler) {
this.sendMessage({
type: CALL_TYPES.CALLBACK_RESPONSE,
name: callbackName,
error: { message: `${callbackName} is released` },
});
return;
}
let result;
try {
result = await handler(...args);
}
catch (err) {
this.sendMessage({
type: CALL_TYPES.CALLBACK_RESPONSE,
name: callbackName,
error: { message: err.message, stack: this.isSendErrorStack ? err.stack : undefined },
});
return;
}
this.sendMessage({
type: CALL_TYPES.RESPONSE,
name: callbackName,
error: null,
data: result,
});
},
async handleCallbackData(callbackName, timeout, args) {
return new Promise((resolve, reject) => {
const handler = ((err, data) => {
if (handler.timeout)
clearTimeout(handler.timeout);
this.events.delete(callbackName);
if (err == null)
resolve(data);
else {
const error = new Error(err.message);
error.stack = err.stack;
reject(error);
}
});
this.events.set(callbackName, handler);
if (timeout) {
handler.timeout = setTimeout(() => {
handler.timeout = null;
handler('timeout');
}, timeout);
}
this.sendMessage({
type: CALL_TYPES.CALLBACK_REQUEST,
name: callbackName,
args,
});
});
},
async handleRequest(eventName, path, args, callbacks) {
let obj = this.funcsObj;
const name = path.pop();
for (const _name of path) {
obj = obj[_name];
if (obj === undefined) {
this.sendMessage({
type: CALL_TYPES.RESPONSE,
name: eventName,
error: { message: `${name} is not defined` },
});
Expand All @@ -45,6 +111,16 @@ const funcsTools = {
}
if (typeof obj[name] == 'function') {
let result;
if (callbacks.length) {
for (const index of callbacks) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const context = this;
const name = args[index];
args.splice(index, 1, async function (...args) {
return context.handleCallbackData(name, context.timeout, args);
});
}
}
// console.log('args: ', args)
try {
if (this.onCallBeforeParams)
Expand All @@ -53,25 +129,29 @@ const funcsTools = {
}
catch (err) {
this.sendMessage({
type: CALL_TYPES.RESPONSE,
name: eventName,
error: { message: err.message, stack: this.isSendErrorStack ? err.stack : undefined },
});
return;
}
this.sendMessage({
type: CALL_TYPES.RESPONSE,
name: eventName,
error: null,
data: result,
});
}
else if (obj[name] === undefined) {
this.sendMessage({
type: CALL_TYPES.RESPONSE,
name: eventName,
error: { message: `${name} is not defined` },
});
}
else {
this.sendMessage({
type: CALL_TYPES.RESPONSE,
name: eventName,
error: null,
data: obj[name],
Expand Down Expand Up @@ -104,7 +184,7 @@ const funcsTools = {
}
group.handling = true;
},
async handleData(groupName, pathname, timeout, data) {
async handleData(groupName, pathname, timeout, args) {
const eventName = `${pathname.join('.')}__${String(Math.random()).substring(2)}`;
return new Promise((resolve, reject) => {
const handler = ((err, data) => {
Expand All @@ -120,6 +200,16 @@ const funcsTools = {
reject(error);
}
});
const callbacks = [];
args = args.map((arg, index) => {
if (typeof arg === 'function') {
if (!arg.__msg2call_cbname__)
throw new Error('callback is not a proxy callback');
callbacks.push(index);
return arg.__msg2call_cbname__;
}
return arg;
});
this.events.set(eventName, handler);
if (timeout) {
handler.timeout = setTimeout(() => {
Expand All @@ -128,13 +218,15 @@ const funcsTools = {
}, timeout);
}
this.sendMessage({
type: CALL_TYPES.REQUEST,
name: eventName,
path: pathname,
data,
args,
cbs: callbacks,
});
});
},
async getData(groupName, pathname, data) {
async getData(groupName, pathname, args) {
// console.log(groupName, pathname, data)
let timeout = this.timeout;
let isQueue = false;
Expand All @@ -147,7 +239,7 @@ const funcsTools = {
await this.waitQueue(group, groupName, pathname);
}
}
let promise = this.handleData(groupName, pathname, timeout, data);
let promise = this.handleData(groupName, pathname, timeout, args);
if (isQueue) {
promise = promise.then((data) => {
this.handleGroupNextTask(groupName);
Expand All @@ -160,23 +252,23 @@ const funcsTools = {
}
return promise;
},
async handleGetData(name, err, data) {
async handleResponse(name, err, data) {
const handler = this.events.get(name);
if (handler) {
if (typeof handler == 'function')
handler(err, data);
// else if (Array.isArray(handler)) {
// for (const h of handler) await h(data)
// }
}
// if (handler) {
if (typeof handler == 'function')
handler(err, data);
// else if (Array.isArray(handler)) {
// for (const h of handler) await h(data)
// }
// }
},
createProxy(context, groupName, path = []) {
const proxy = new Proxy(function () { }, {
get: (_target, prop, receiver) => {
let propName = prop.toString();
// console.log('get prop name', propName, path)
if (prop == 'then' && path.length) {
const r = context.getData(groupName, path);
const r = context.getData(groupName, path, []);
return r.then.bind(r);
}
return context.createProxy(context, groupName, [...path, propName]);
Expand All @@ -189,12 +281,18 @@ const funcsTools = {
});
return proxy;
},
onMessage({ name, path, error, data }) {
if (name) {
if (path?.length)
void this.handleResponseData(name, path, data);
else
void this.handleGetData(name, error, data);
onMessage(message) {
switch (message.type) {
case CALL_TYPES.REQUEST:
void this.handleRequest(message.name, message.path, message.args, message.cbs);
break;
case CALL_TYPES.CALLBACK_REQUEST:
void this.handleCallbackRequest(message.name, message.args);
break;
case CALL_TYPES.RESPONSE:
case CALL_TYPES.CALLBACK_RESPONSE:
void this.handleResponse(message.name, message.error, message.data);
break;
}
},
onDestroy() {
Expand Down Expand Up @@ -227,5 +325,22 @@ const createMsg2call = (options) => {
destroy: tools.onDestroy.bind(tools),
};
};
/**
* create a proxy callback
*/
const createProxyCallback = (callback) => {
const name = callback.__msg2call_cbname__ = `func_${proxyCallbacks.size}_${String(Math.random()).substring(2, 10)}`;
proxyCallbacks.set(name, callback);
callback.releaseProxy = () => {
proxyCallbacks.delete(name);
};
return callback;
};
/**
* release all created proxy callback
*/
const releaseAllProxyCallback = () => {
proxyCallbacks.clear();
};

export { createMsg2call };
export { createMsg2call, createProxyCallback, releaseAllProxyCallback };
Loading

0 comments on commit 6eef3bc

Please sign in to comment.