In a Jupyter Notebook, we can execute Javascript/HTML cells directly to enrich the Notebook display, e.g. an interactive Vis.js
graph, thanks to the following import
:
from IPython.display import Javascript
from IPython.core.display import display, HTML
I have the following Notebook cells:
- Cell 1: Register an utility function so that later it can be called multiple times.
%%javascript
requirejs.config({
paths: {
vis: 'vis'
}
});
require(['vis'], function(vis){
function drawNetworkGraph() {
...
}
window.drawNetworkGraph = drawNetworkGraph;
});
- Cell 2: Try invoking the utility function.
transfer_data_from_python_to_javascript = "..."
display(Javascript(transfer_data_from_python_to_javascript))
display(Javascript("""window.drawNetworkGraph();""")) // <-- problem here!
When executing "Run All" in the Notebook, I got a Race Condition where window.drawNetworkGraph is not a function
in DevTools Console.
This is due to require(, function(){})
in Cell 1 ran asynchronously. It takes some time for the browser to load the vis
library; and when it is finished loading, it will invoke the function declared in the 2nd argument of require()
. At this point of time, the Jupyter Notebook already executes the Cell 2, leading to not a function
.
Question
In Javascript world, it's highly recommended to write code in asynchronous manner. That's why require()
supports callback. What is the best practice to overcome such setup in Jupyter Notebook?
My goal is to embed vis.js
graph. Feel free to suggest anything. Thank you.
My attempts
1). Remove Cell 1. In Cell 2 or similar Cell, whenever wanting to invoke such utility function, we enclose all javascript in Cell 1. Downside: cumbersome because we have many other latter cells which invoke the utility function.
2). Use "wait for" mechanism in javascript: check if window.drawGraphNetwork
is defined or not; if available, call it; if not, setTimeOut
to call it a little bit later. Downside: a little bit more complicated.
Nevertheless, these approaches are less elegant.