I’m hoping this will come as a refreshing change because I’m approaching this forum with a solution rather than a problem. What do you all think of the idea I’m exploring below? I have adapted one example from the JavaScript Callbacks documentation page to pure javascript to execute callbacks in a separate web worker. The web worker message-passing API neatly leverages Bokeh’s protocol and acts as surrogate bokeh server. Comments and reactions welcome.
<html lang="en">
<head>
<meta charset="UTF-8">
<**link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bokeh/0.12.16/bokeh.min.css" **/>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bokeh/0.12.16/bokeh-widgets.min.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bokeh/0.12.16/bokeh.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bokeh/0.12.16/bokeh-api.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bokeh/0.12.16/bokeh-widgets.js"></script>
</head>
<body>
<div class="bk-root" id="bokeh-root"></div>
<script type="text/js-worker">
{
// Stub out a few globals so importScripts doesn’t barf
self.setImmediate = (func, …params) => setTimeout(func, 0, …params);
self.document = {addEventListener() {}, createElement() {return {style: {}}}, createTextNode() {}};
self.window = {};
self.Image = {};
importScripts(
‘https://cdnjs.cloudflare.com/ajax/libs/bokeh/0.12.16/bokeh.min.js’,
‘https://cdnjs.cloudflare.com/ajax/libs/bokeh/0.12.16/bokeh-api.min.js’,
'https://cdnjs.cloudflare.com/ajax/libs/bokeh/0.12.16/bokeh-widgets.min.js’);
const
doc = new Bokeh.Document(),
setter_id = ‘worker’;
doc.on_change(event => ‘worker’ === event.setter_id || postMessage(doc.create_json_patch([event])));
self.onmessage = event => doc.apply_json_patch(event.data, , ‘worker’)
}
</script>
<script>
{
**const
*****doc ***= **new **Bokeh.Document(),
***blob ***= new Blob([document.querySelector(“script[type=‘text/js-worker’]”).textContent], {type: ‘text/javascript’}),
***worker ***= **new **Worker(URL.createObjectURL(blob));
···
- *doc.on_change(event => **‘main’ **=== event.setter_id || worker.postMessage(doc.create_json_patch([event])));
worker.addEventListener(‘message’, message => doc.apply_json_patch(message.data, , ‘main’));
**function **cb(source, cb_obj) {
*// make sure it only runs in Worker
***if (‘undefined’ **!== **typeof *****WorkerGlobalScope ***&& ***self *****instanceof **WorkerGlobalScope) {
**const
**x = source.data.x,
f = cb_obj.value,
data = {x, y: x.map(xi => Math.pow(xi, f))};
source.setv({data}, {setter_id: ‘callback’})
}
}
**const
*****x ***= Bokeh.LinAlg.range(0, 200).map(x => x * .005),
***y ***= […x],
***source ***= **new **Bokeh.ColumnDataSource({data: {x, y}}),
***plot ***= Bokeh.Plotting.figure({plot_width: 400, plot_height: 400}),
***callback ***= **new **Bokeh.CustomJS({args: {source}, code:(**${*cb*.toString()}**)(source, cb_obj, cb_data)
}),
***slider ***= **new **Bokeh.Widgets.Slider({start: .1, end: 4, value: 1, step: .1, title: ‘power’, deferred: true}),
col = new **Bokeh.Column({children: [slider, plot]});
*// install CustomJS so it gets serialized to worker
*slider.js_property_callbacks[‘change:value’] = [callback];
slider.finalize();
plot.line({field: ‘x’}, {field: ‘y’}, {source, line_width: 3, line_alpha: 0.6});
doc.add_root(col);
Bokeh.embed.add_document_standalone(doc, document.querySelector(’#bokeh-root’));
}
</script>
</body>
</html>