Skip to content

Commit 79949c1

Browse files
committed
Improve async behavior in call handling
1 parent c6e701d commit 79949c1

1 file changed

Lines changed: 93 additions & 41 deletions

File tree

src/widget.ts

Lines changed: 93 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,22 @@ export class ActiveHTMLModel extends DOMWidgetModel {
7676
// constructor() {
7777
// super();
7878
// }
79+
_is_ready=false;
7980
initialize(attributes: any, options: { model_id: string; comm?: any; widget_manager: any }) {
8081
super.initialize(attributes, options);
8182
// this._ihandlers= {};
82-
this._updateHandlers();
83-
this.on('change:jsHandlers', this._updateHandlers, this);
83+
this.ready()
84+
}
85+
ready():Promise<ActiveHTMLModel> {
86+
if (!this._is_ready) {
87+
return this._updateHandlers().then(()=> {
88+
this.on('change:jsHandlers', this._updateHandlers, this);
89+
this._is_ready = true;
90+
return this
91+
})
92+
} else {
93+
return Promise.resolve(this)
94+
}
8495
}
8596

8697
defaults() {
@@ -160,12 +171,11 @@ export class ActiveHTMLModel extends DOMWidgetModel {
160171
}
161172
return hash;
162173
}
163-
_updateHandlers(): void {
164-
let handlers = this.get('jsHandlers') as Record<string, string>;
165-
let debug = this.get('_debugPrint');
174+
_setHandlers(handlers:Record<string, string>) {
166175
let _ihandlers = this.get('_ihandlers');
176+
let debug = this.get('_debugPrint');
167177
for (let h in handlers) {
168-
if (handlers.hasOwnProperty(h)) {
178+
if (h!="src"&&handlers.hasOwnProperty(h)) {
169179
let hash = this._stringHash(handlers[h]);
170180
if (
171181
(!_ihandlers.hasOwnProperty(h)) ||
@@ -179,6 +189,52 @@ export class ActiveHTMLModel extends DOMWidgetModel {
179189
}
180190
}
181191
}
192+
_apiMod:string|null = null;
193+
static _needsAPILoad(curMod:string|null, handlers:Record<string, any>, _ihandlers:Record<string, any> ):boolean {
194+
return (
195+
handlers.hasOwnProperty("src") &&
196+
curMod !== handlers["src"] &&
197+
(!_ihandlers.hasOwnProperty("src")||_ihandlers["src"]!=handlers["src"])
198+
)
199+
}
200+
_apiLoader:Promise<Record<string, any>>|null = null;
201+
_updateHandlers(): Promise<Record<string, any>> {
202+
let handlers = this.get('jsHandlers') as Record<string, string>;
203+
let debug = this.get('_debugPrint');
204+
let _ihandlers = this.get('_ihandlers') as Record<string, any>;
205+
let imp = null;
206+
if (ActiveHTMLModel._needsAPILoad(this._apiMod, handlers, _ihandlers)) {
207+
if (debug) {
208+
console.log('loading API from source', handlers["src"]);
209+
}
210+
// Ugly TypeScript hack to keep the dynamic import semantics we need
211+
// for reliable loading
212+
imp = eval("import(\"" + handlers["src"] + "\")");
213+
}
214+
if (imp !== null) {
215+
this._apiLoader = imp.then(
216+
(mod: Record<string, any>) => {
217+
for (let m in mod) {
218+
if (mod[m] instanceof Function) {
219+
_ihandlers[m] = [null, mod[m]];
220+
}
221+
}
222+
this._apiMod = handlers["src"];
223+
_ihandlers["src"] = handlers["src"];
224+
}
225+
).then(() => {
226+
this._setHandlers(handlers);
227+
this._apiLoader = null;
228+
return _ihandlers
229+
})
230+
}
231+
if (this._apiLoader !== null && typeof this._apiLoader !== "undefined") {
232+
return this._apiLoader as Promise<Record<string, any>>;
233+
} else {
234+
this._setHandlers(handlers)
235+
return Promise.resolve(_ihandlers)
236+
}
237+
}
182238

183239
_handle_comm_msg(msg: KernelMessage.ICommMsgMsg): Promise<void> {
184240
const data = msg.content.data as any;
@@ -209,27 +265,40 @@ export class ActiveHTMLModel extends DOMWidgetModel {
209265
...ops
210266
} as unknown as Event; // a hack only so we can use the same interface for custom events
211267
}
212-
callHandler(method:string, event:Event) {
213-
let handlers = this.get('_ihandlers') as Record<string, any>;
214-
let fn: ((event:Event, widget:WidgetModel, context:object) => void)|null = null;
268+
callHandler(method:string, event:Event): Promise<any> {
269+
return ActiveHTMLModel.callModelHandler(method, event, this, this);
270+
}
271+
static callModelHandler(method: string, event: Event, model:WidgetModel, target:any): Promise<any> {
272+
let handlers = model.get('_ihandlers') as Record<string, any>;
273+
let fn: ((event:Event, widget:WidgetModel, context:object) => any)|null = null;
274+
215275
if (handlers.hasOwnProperty(method)) {
216276
fn = handlers[method][1];
277+
if (fn !== null) {
278+
let val = fn.call(target, event, target, ActiveHTMLView.handlerContext);
279+
return Promise.resolve(val);
280+
} else {
281+
throw new Error("handler " + method + " is null");
282+
}
217283
} else {
218-
let api = this.get('jsAPI');
284+
let api = model.get('jsAPI') as ActiveHTMLModel|null;
219285
if (api !== null) {
220-
handlers = api.get("_ihandlers");
221-
if (handlers.hasOwnProperty(method)) {
222-
fn = handlers[method][1];
223-
}
286+
return api.ready().then((api) => {
287+
handlers = api.get('_ihandlers') as Record<string, any>
288+
if (handlers.hasOwnProperty(method)) {
289+
fn = handlers[method][1];
290+
}
291+
if (fn !== null) {
292+
return fn.call(target, event, target, ActiveHTMLView.handlerContext);
293+
} else {
294+
throw new Error("couldn't find API method " + method);
295+
}
296+
})
297+
} else {
298+
throw new Error("couldn't find handler or API method " + method);
224299
}
225300
}
226-
if (fn !== null) {
227-
fn.call(this, event, this, ActiveHTMLView.handlerContext);
228-
} else {
229-
throw new Error("couldn't find handler " + method);
230-
}
231301
}
232-
233302
}
234303

235304
export class ActiveHTMLView extends DOMWidgetView {
@@ -699,7 +768,7 @@ export class ActiveHTMLView extends DOMWidgetView {
699768
}
700769
updateEvents():Promise<any> {
701770
return this.setEvents().then(
702-
()=>this.removeEvents
771+
()=>this.removeEvents()
703772
)
704773
}
705774

@@ -754,7 +823,7 @@ export class ActiveHTMLView extends DOMWidgetView {
754823
}
755824
updateOnHandlers():Promise<any> {
756825
return this.setOnHandlers().then(
757-
()=>this.removeOnHandlers
826+
()=>this.removeOnHandlers()
758827
)
759828
}
760829

@@ -950,25 +1019,8 @@ export class ActiveHTMLView extends DOMWidgetView {
9501019
this.sendEventMessage(e, this.constructEventMessage(e, props, eventName));
9511020
}
9521021
}
953-
callHandler(method:string, event:Event) {
954-
let handlers = this.model.get('_ihandlers') as Record<string, any>;
955-
let fn: ((event:Event, widget:WidgetView, context:object) => void)|null = null;
956-
if (handlers.hasOwnProperty(method)) {
957-
fn = handlers[method][1];
958-
} else {
959-
let api = this.model.get('jsAPI');
960-
if (api !== null) {
961-
handlers = api.get("_ihandlers");
962-
if (handlers.hasOwnProperty(method)) {
963-
fn = handlers[method][1];
964-
}
965-
}
966-
}
967-
if (fn !== null) {
968-
fn.call(this, event, this, ActiveHTMLView.handlerContext);
969-
} else {
970-
throw new Error("couldn't find handler " + method);
971-
}
1022+
callHandler(method:string, event:Event):Promise<any> {
1023+
return ActiveHTMLModel.callModelHandler(method, event, this.model, this);
9721024
}
9731025
dummyEvent(name:string, ops:object={}):Event {
9741026
return {

0 commit comments

Comments
 (0)