Created
September 16, 2020 11:20
-
-
Save gaplo917/04f63e0f01ecd751853550a4b78df994 to your computer and use it in GitHub Desktop.
Web Worker RPC Impl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const status = { | |
OK: 0, | |
CANCELLED: 1, | |
INVALID_ARGUMENT: 3, | |
UNIMPLEMENTED: 12, | |
INTERNAL: 13, | |
} | |
/** | |
* | |
* @param self Web Worker Scope | |
* @param methods RPC Methods | |
*/ | |
export function wRPCServer(self, methods) { | |
self.addEventListener('message', function ({ data }) { | |
let id = data.id | |
if (data.type !== 'wRPC' || id == null) return | |
if (data.method) { | |
let method = methods[data.method] | |
if (method == null) { | |
self.postMessage( | |
{ | |
type: 'wRPC', | |
id, | |
error: 'WRPC_STATUS_UNIMPLEMENTED', | |
status: status.UNIMPLEMENTED, | |
}, | |
null, | |
) | |
} else { | |
Promise.resolve() | |
.then(() => method(...data.params)) | |
.then(result => { | |
self.postMessage({ type: 'wRPC', id, result, status: status.OK }, null) | |
}) | |
.catch(err => { | |
self.postMessage({ type: 'wRPC', id, error: '' + err, status: status.INTERNAL }, null) | |
}) | |
} | |
} else { | |
self.postMessage( | |
{ | |
type: 'wRPC', | |
id, | |
error: 'WRPC_STATUS_INVALID_ARGUMENT, invalid `method` argument ', | |
status: status.INVALID_ARGUMENT, | |
}, | |
null, | |
) | |
} | |
}) | |
} | |
const handleRPCCallbacks = (event, callbacks) => { | |
if (event.data && event.data.type === 'wRPC') { | |
const cbTuple = callbacks.get(event.data.id) | |
// callback must fulfil the tuple structure [resolve, reject] | |
if (cbTuple && typeof cbTuple[0] === 'function' && typeof cbTuple[1] === 'function') { | |
if (event.data.status === status.OK) { | |
cbTuple[0](event.data.result) | |
} else if (event.data.status > status.OK) { | |
cbTuple[1](event.data.error) | |
} | |
} | |
// remove the callbacks | |
callbacks.delete(event.data.id) | |
} | |
} | |
/** | |
* rpc call that return standard promise | |
* @param worker Worker | |
* @returns { * | Worker } | |
*/ | |
export function wRPC(worker) { | |
const callbacks = new Map() | |
let counter = 0 | |
const call = method => (...params) => { | |
return new Promise((resolve, reject) => { | |
const id = ++counter | |
callbacks.set(id, [resolve, reject]) | |
worker.postMessage({ type: 'wRPC', id, method, params }) | |
}) | |
} | |
worker.onmessage = event => handleRPCCallbacks(event, callbacks) | |
return new Proxy(worker, { | |
get(target, p, receiver) { | |
return call(p) | |
}, | |
}) | |
} | |
/** | |
* rpc call that return react suspense compatible contract | |
* @param worker Worker | |
* @returns { * | Worker } | |
*/ | |
export function wRPCSuspense(worker) { | |
const callbacks = new Map() | |
let counter = 0 | |
const call = method => (...params) => { | |
let status = 'pending' | |
let result = null | |
const suspender = new Promise((resolve, reject) => { | |
const id = ++counter | |
callbacks.set(id, [ | |
r => { | |
status = 'success' | |
result = r | |
resolve() | |
}, | |
err => { | |
status = 'error' | |
result = err | |
reject() | |
}, | |
]) | |
worker.postMessage({ type: 'wRPC', id, method, params }) | |
}) | |
return () => { | |
if (status === 'pending') { | |
throw suspender | |
} else if (status === 'error') { | |
throw result | |
} else if (status === 'success') { | |
return result | |
} | |
} | |
} | |
worker.onmessage = event => handleRPCCallbacks(event, callbacks) | |
return new Proxy(worker, { | |
get(target, p, receiver) { | |
return call(p) | |
}, | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment