Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9d42bcb

Browse files
committed
Merge pull request #6818 from jdfreder/race_conditions
Add promises to the widget framework.
2 parents cad04cd + aa5a8ce commit 9d42bcb

19 files changed

Lines changed: 610 additions & 386 deletions

IPython/html/static/base/js/utils.js

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,119 @@ define([
605605
$.ajax(url, settings);
606606
});
607607
};
608-
608+
609+
var WrappedError = function(message, error){
610+
// Wrappable Error class
611+
612+
// The Error class doesn't actually act on `this`. Instead it always
613+
// returns a new instance of Error. Here we capture that instance so we
614+
// can apply it's properties to `this`.
615+
var tmp = Error.apply(this, [message]);
616+
617+
// Copy the properties of the error over to this.
618+
var properties = Object.getOwnPropertyNames(tmp);
619+
for (var i = 0; i < properties.length; i++) {
620+
this[properties[i]] = tmp[properties[i]];
621+
}
622+
623+
// Keep a stack of the original error messages.
624+
if (error instanceof WrappedError) {
625+
this.error_stack = error.error_stack;
626+
} else {
627+
this.error_stack = [error];
628+
}
629+
this.error_stack.push(tmp);
630+
631+
return this;
632+
};
633+
634+
WrappedError.prototype = Object.create(Error.prototype, {});
635+
636+
637+
var load_class = function(class_name, module_name, registry) {
638+
// Tries to load a class
639+
//
640+
// Tries to load a class from a module using require.js, if a module
641+
// is specified, otherwise tries to load a class from the global
642+
// registry, if the global registry is provided.
643+
return new Promise(function(resolve, reject) {
644+
645+
// Try loading the view module using require.js
646+
if (module_name) {
647+
require([module_name], function(module) {
648+
if (module[class_name] === undefined) {
649+
reject(new Error('Class '+class_name+' not found in module '+module_name));
650+
} else {
651+
resolve(module[class_name]);
652+
}
653+
}, reject);
654+
} else {
655+
if (registry && registry[class_name]) {
656+
resolve(registry[class_name]);
657+
} else {
658+
reject(new Error('Class '+class_name+' not found in registry '));
659+
}
660+
}
661+
});
662+
};
663+
664+
var resolve_promises_dict = function(d) {
665+
// Resolve a promiseful dictionary.
666+
// Returns a single Promise.
667+
var keys = Object.keys(d);
668+
var values = [];
669+
keys.forEach(function(key) {
670+
values.push(d[key]);
671+
});
672+
return Promise.all(values).then(function(v) {
673+
d = {};
674+
for(var i=0; i<keys.length; i++) {
675+
d[keys[i]] = v[i];
676+
}
677+
return d;
678+
});
679+
};
680+
681+
var WrappedError = function(message, error){
682+
// Wrappable Error class
683+
684+
// The Error class doesn't actually act on `this`. Instead it always
685+
// returns a new instance of Error. Here we capture that instance so we
686+
// can apply it's properties to `this`.
687+
var tmp = Error.apply(this, [message]);
688+
689+
// Copy the properties of the error over to this.
690+
var properties = Object.getOwnPropertyNames(tmp);
691+
for (var i = 0; i < properties.length; i++) {
692+
this[properties[i]] = tmp[properties[i]];
693+
}
694+
695+
// Keep a stack of the original error messages.
696+
if (error instanceof WrappedError) {
697+
this.error_stack = error.error_stack;
698+
} else {
699+
this.error_stack = [error];
700+
}
701+
this.error_stack.push(tmp);
702+
703+
return this;
704+
};
705+
706+
WrappedError.prototype = Object.create(Error.prototype, {});
707+
708+
var reject = function(message, log) {
709+
// Creates a wrappable Promise rejection function.
710+
//
711+
// Creates a function that returns a Promise.reject with a new WrappedError
712+
// that has the provided message and wraps the original error that
713+
// caused the promise to reject.
714+
return function(error) {
715+
var wrapped_error = new WrappedError(message, error);
716+
if (log) console.error(wrapped_error);
717+
return Promise.reject(wrapped_error);
718+
};
719+
};
720+
609721
var utils = {
610722
regex_split : regex_split,
611723
uuid : uuid,
@@ -635,6 +747,10 @@ define([
635747
XHR_ERROR : XHR_ERROR,
636748
wrap_ajax_error : wrap_ajax_error,
637749
promising_ajax : promising_ajax,
750+
WrappedError: WrappedError,
751+
load_class: load_class,
752+
resolve_promises_dict: resolve_promises_dict,
753+
reject: reject,
638754
};
639755

640756
// Backwards compatability.

IPython/html/static/services/kernels/comm.js

Lines changed: 45 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ define([
5151

5252
CommManager.prototype.register_comm = function (comm) {
5353
// Register a comm in the mapping
54-
this.comms[comm.comm_id] = comm;
54+
this.comms[comm.comm_id] = Promise.resolve(comm);
5555
comm.kernel = this.kernel;
5656
return comm.comm_id;
5757
};
@@ -66,67 +66,62 @@ define([
6666
CommManager.prototype.comm_open = function (msg) {
6767
var content = msg.content;
6868
var that = this;
69-
70-
var instantiate_comm = function(target) {
71-
var comm = new Comm(content.target_name, content.comm_id);
72-
that.register_comm(comm);
69+
var comm_id = content.comm_id;
70+
71+
this.comms[comm_id] = utils.load_class(content.target_name, content.target_module,
72+
this.targets).then(function(target) {
73+
74+
var comm = new Comm(content.target_name, comm_id);
75+
comm.kernel = that.kernel;
7376
try {
74-
target(comm, msg);
77+
var response = target(comm, msg);
78+
if (response instanceof Promise) {
79+
return response.then(function() { return Promise.resolve(comm); });
80+
}
7581
} catch (e) {
76-
console.log("Exception opening new comm:", e, e.stack, msg);
7782
comm.close();
7883
that.unregister_comm(comm);
84+
var wrapped_error = new utils.WrappedError("Exception opening new comm", e);
85+
console.error(wrapped_error);
86+
return Promise.reject(wrapped_error);
7987
}
80-
};
81-
82-
if (content.target_module) {
83-
// Load requirejs module for comm target
84-
require([content.target_module], function(mod) {
85-
var target = mod[content.target_name];
86-
if (target !== undefined) {
87-
instantiate_comm(target)
88-
} else {
89-
console.log("Comm target " + content.target_name +
90-
" not found in module " + content.target_module);
91-
}
92-
}, function(err) { console.log(err); });
93-
} else {
94-
// No requirejs module specified: look for target in registry
95-
var f = this.targets[content.target_name];
96-
if (f === undefined) {
97-
console.log("No such target registered: ", content.target_name);
98-
console.log("Available targets are: ", this.targets);
99-
return;
100-
}
101-
instantiate_comm(f)
102-
}
88+
return Promise.resolve(comm);
89+
}, utils.reject('Could not open comm', true));
90+
return this.comms[comm_id];
10391
};
10492

105-
CommManager.prototype.comm_close = function (msg) {
93+
CommManager.prototype.comm_close = function(msg) {
10694
var content = msg.content;
107-
var comm = this.comms[content.comm_id];
108-
if (comm === undefined) {
95+
if (this.comms[content.comm_id] === undefined) {
96+
console.error('Comm promise not found for comm id ' + content.comm_id);
10997
return;
11098
}
111-
this.unregister_comm(comm);
112-
try {
113-
comm.handle_close(msg);
114-
} catch (e) {
115-
console.log("Exception closing comm: ", e, e.stack, msg);
116-
}
99+
100+
this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
101+
this.unregister_comm(comm);
102+
try {
103+
comm.handle_close(msg);
104+
} catch (e) {
105+
console.log("Exception closing comm: ", e, e.stack, msg);
106+
}
107+
});
117108
};
118109

119-
CommManager.prototype.comm_msg = function (msg) {
110+
CommManager.prototype.comm_msg = function(msg) {
120111
var content = msg.content;
121-
var comm = this.comms[content.comm_id];
122-
if (comm === undefined) {
112+
if (this.comms[content.comm_id] === undefined) {
113+
console.error('Comm promise not found for comm id ' + content.comm_id);
123114
return;
124115
}
125-
try {
126-
comm.handle_msg(msg);
127-
} catch (e) {
128-
console.log("Exception handling comm msg: ", e, e.stack, msg);
129-
}
116+
117+
this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
118+
try {
119+
comm.handle_msg(msg);
120+
} catch (e) {
121+
console.log("Exception handling comm msg: ", e, e.stack, msg);
122+
}
123+
return Promise.resolve(comm);
124+
});
130125
};
131126

132127
//-----------------------------------------------------------------------
@@ -180,7 +175,7 @@ define([
180175

181176
// methods for handling incoming messages
182177

183-
Comm.prototype._maybe_callback = function (key, msg) {
178+
Comm.prototype._callback = function (key, msg) {
184179
var callback = this['_' + key + '_callback'];
185180
if (callback) {
186181
try {
@@ -192,11 +187,11 @@ define([
192187
};
193188

194189
Comm.prototype.handle_msg = function (msg) {
195-
this._maybe_callback('msg', msg);
190+
this._callback('msg', msg);
196191
};
197192

198193
Comm.prototype.handle_close = function (msg) {
199-
this._maybe_callback('close', msg);
194+
this._callback('close', msg);
200195
};
201196

202197
// For backwards compatability.

0 commit comments

Comments
 (0)