Skip to content

Commit

Permalink
Merge pull request #126 from martinRenou/improve_worker
Browse files Browse the repository at this point in the history
Improve worker implementation
  • Loading branch information
SylvainCorlay committed May 20, 2019
2 parents 3360626 + 8aeb8b2 commit bea120e
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 84 deletions.
147 changes: 127 additions & 20 deletions js/src/renderer.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,140 @@
import * as widgets from '@jupyter-widgets/base';
import * as Handsontable from 'handsontable';
import {extend} from 'lodash';
import {extend, forEach} from 'lodash';
import {semver_range} from './version';
import {safeEval} from './worker_eval';

let RendererModel = widgets.WidgetModel.extend({
defaults: function() {
return extend(RendererModel.__super__.defaults.call(this), {

export class ExecuteRequest {
constructor(code: string) {
this.id = widgets.uuid();
this.code = code;

this.execute_promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}

id: string;
code: string;
execute_promise: Promise<any>;
resolve: Function;
reject: Function;
};


export class SafeJSKernel {
constructor() {
this.initialize();
}

execute(code: string) {
const request = new ExecuteRequest(code);

this.requests[request.id] = request;

this.worker.postMessage({ id: request.id, code: request.code });

return request.execute_promise;
}

initialize() {
const blobURL = URL.createObjectURL(new Blob([
'(',
function () {
const _postMessage = postMessage;
const _addEventListener = addEventListener;

((obj) => {
'use strict';

let current = obj;
const keepProperties = [
// required
'Object', 'Function', 'Infinity', 'NaN', 'undefined', 'caches', 'TEMPORARY', 'PERSISTENT',
// optional, but trivial to get back
'Array', 'Boolean', 'Number', 'String', 'Symbol',
// optional
'Map', 'Math', 'Set',
];

do {
Object.getOwnPropertyNames(current).forEach((name) => {
if (keepProperties.indexOf(name) === -1) {
delete current[name];
}
});
current = Object.getPrototypeOf(current);
}
while (current !== Object.prototype);
})(this);

_addEventListener('message', ({ data }) => {
const f = new Function('', `return (${data.code}\n);`);
_postMessage({ id: data.id, result: f() }, undefined);
});
}.toString(),
')()'
], {
type: 'application/javascript'
}));

this.worker = new Worker(blobURL);

this.worker.onmessage = ({ data }) => {
// Resolve the right Promise with the return value
this.requests[data.id].resolve(data.result);
delete this.requests[data.id];
};

this.worker.onerror = ({ message }) => {
// Reject all the pending promises, terminate the worker and start again
forEach(this.requests, (request) => {
request.reject(message);
});
this.requests = {};

this.worker.terminate();

this.initialize();
};

URL.revokeObjectURL(blobURL);
}

worker: Worker;
requests: { [key:string] : ExecuteRequest; } = {};
}


export class RendererModel extends widgets.WidgetModel {
defaults() {
return {...widgets.WidgetModel.prototype.defaults(),
_model_name : 'RendererModel',
_model_module : 'ipysheet',
_model_module_version : semver_range,
name: '',
code: ''
});
},
initialize: function() {
RendererModel.__super__.initialize.apply(this, arguments);
// We add Handsontable manually as extra argument to put it in the scope
var that = this;
this.fn = function (instance, td, row, col, prop, value, cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);
safeEval(`(${that.get('code')})(${value})`).then(function(style) {
(Object as any).assign(td.style, style);
});
};
(Handsontable.renderers as any).registerRenderer(this.get('name'), this.fn);
}
});

export {
RendererModel
initialize(attributes: any, options: any) {
super.initialize(attributes, options);

this.kernel = new SafeJSKernel();

const that = this;
this.rendering_function = function (instance, td, row, col, prop, value, cellProperties) {
Handsontable.renderers.TextRenderer.apply(this, arguments);

that.kernel.execute(`(${that.get('code')})(${value})`).then((style) => {
(Object as any).assign(td.style, style);
});
};

(Handsontable.renderers as any).registerRenderer(this.get('name'), this.rendering_function);
}

kernel: SafeJSKernel;
rendering_function: Function;
};
2 changes: 1 addition & 1 deletion js/src/test/test_renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('custom', function() {
});

it('register', function() {
expect(this.renderer.fn).to.not.equal(undefined);
expect(this.renderer.rendering_function).to.not.equal(undefined);
expect((Handsontable.renderers as any).getRenderer('test_renderer')).to.not.equal(undefined);
});
});
Expand Down
63 changes: 0 additions & 63 deletions js/src/worker_eval.ts

This file was deleted.

0 comments on commit bea120e

Please sign in to comment.