diff --git a/IPython/html/static/widgets/js/manager.js b/IPython/html/static/widgets/js/manager.js
index 60c0f44..07dc383 100644
--- a/IPython/html/static/widgets/js/manager.js
+++ b/IPython/html/static/widgets/js/manager.js
@@ -172,11 +172,19 @@ define([
WidgetManager.prototype.get_model = function (model_id) {
// Look-up a model instance by its id.
- var model = this._models[model_id];
- if (model !== undefined && model.id == model_id) {
- return model;
+ var that = this;
+ var model = that._models[model_id];
+ if (model !== undefined) {
+ return new Promise(function(resolve, reject){
+ if (model instanceof Promise) {
+ model.then(resolve, reject);
+ } else {
+ resolve(model);
+ }
+ });
+ } else {
+ return undefined;
}
- return null;
};
WidgetManager.prototype._handle_comm_open = function (comm, msg) {
@@ -213,30 +221,32 @@ define([
// widget_class: (optional) string
// Target name of the widget in the back-end.
// comm: (optional) Comm
- return new Promise(function(resolve, reject) {
+
+ // Create a comm if it wasn't provided.
+ var comm = options.comm;
+ if (!comm) {
+ comm = this.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class});
+ }
+
+ var that = this;
+ var model_id = comm.comm_id;
+ var promise = new Promise(function(resolve, reject) {
// Get the model type using require or through the registry.
var widget_type_name = options.model_name;
var widget_module = options.model_module;
- var that = this;
utils.try_load(widget_type_name, widget_module, WidgetManager._model_types)
.then(function(ModelType) {
-
- // Create a comm if it wasn't provided.
- var comm = options.comm;
- if (!comm) {
- comm = that.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class});
- }
-
- var model_id = comm.comm_id;
var widget_model = new ModelType(that, model_id, comm);
widget_model.on('comm:close', function () {
delete that._models[model_id];
});
that._models[model_id] = widget_model;
- reolve(widget_model);
+ resolve(widget_model);
}, reject);
});
+ this._models[model_id] = promise;
+ return promise;
};
// Backwards compatibility.
diff --git a/IPython/html/static/widgets/js/widget.js b/IPython/html/static/widgets/js/widget.js
index 0cebd88..6e99190 100644
--- a/IPython/html/static/widgets/js/widget.js
+++ b/IPython/html/static/widgets/js/widget.js
@@ -92,11 +92,22 @@ define(["widgets/js/manager",
// Handle when a widget is updated via the python side.
this.state_lock = state;
try {
- var that = this;
- WidgetModel.__super__.set.apply(this, [Object.keys(state).reduce(function(obj, key) {
- obj[key] = that._unpack_models(state[key]);
- return obj;
- }, {})]);
+ var state_keys = [];
+ var state_values = [];
+ for (var state_key in Object.keys(state)) {
+ if (state.hasOwnProperty(state_key)) {
+ state_keys.push(state_key);
+ state_values.push(this._unpack_models(state[state_key]));
+ }
+ }
+
+ Promise.all(state_values).then(function(promise_values){
+ var unpacked_state = {};
+ for (var i = 0; i < state_keys.length; i++) {
+ unpacked_state[state_keys[i]] = promise_values[i];
+ }
+ WidgetModel.__super__.set.apply(this, [unpacked_state]);
+ }, console.error);
} finally {
this.state_lock = null;
}
@@ -254,30 +265,42 @@ define(["widgets/js/manager",
// Replace model ids with models recursively.
var that = this;
var unpacked;
- if ($.isArray(value)) {
- unpacked = [];
- _.each(value, function(sub_value, key) {
- unpacked.push(that._unpack_models(sub_value));
- });
- return unpacked;
-
- } else if (value instanceof Object) {
- unpacked = {};
- _.each(value, function(sub_value, key) {
- unpacked[key] = that._unpack_models(sub_value);
- });
- return unpacked;
+ return new Promise(function(resolve, reject) {
- } else if (typeof value === 'string' && value.slice(0,10) === "IPY_MODEL_") {
- var model = this.widget_manager.get_model(value.slice(10, value.length));
- if (model) {
- return model;
+ if ($.isArray(value)) {
+ unpacked = [];
+ _.each(value, function(sub_value, key) {
+ unpacked.push(that._unpack_models(sub_value));
+ });
+ Promise.all(unpacked).then(resolve, reject);
+
+ } else if (value instanceof Object) {
+ unpacked_values = [];
+ unpacked_keys = [];
+ _.each(value, function(sub_value, key) {
+ unpacked_keys.push(key);
+ unpacked_values.push(that._unpack_models(sub_value));
+ });
+
+ Promise.all(unpacked_values).then(function(promise_values) {
+ unpacked = {};
+ for (var i = 0; i < unpacked_keys.length; i++) {
+ unpacked[unpacked_keys[i]] = promise_values[i];
+ }
+ resolve(unpacked);
+ }, reject);
+
+ } else if (typeof value === 'string' && value.slice(0,10) === "IPY_MODEL_") {
+ var model = this.widget_manager.get_model(value.slice(10, value.length));
+ if (model) {
+ model.then(resolve, reject);
+ } else {
+ resolve(value);
+ }
} else {
- return value;
- }
- } else {
- return value;
- }
+ resolve(value);
+ }
+ });
},
on_some_change: function(keys, callback, context) {
@@ -322,11 +345,11 @@ define(["widgets/js/manager",
//
// -given a model and (optionally) a view name if the view name is
// not given, it defaults to the model's default view attribute.
+ var that = this;
return new Promise(function(resolve, reject) {
// TODO: this is hacky, and makes the view depend on this cell attribute and widget manager behavior
// it would be great to have the widget manager add the cell metadata
// to the subview without having to add it here.
- var that = this;
options = $.extend({ parent: this }, options || {});
this.model.widget_manager.create_view(child_model, options).then(function(child_view) {