Couple of ideas here:
How are you embeding the Observable notebooks? If youâre embeding with observablehqâs iframes, you canât do much, but if youâre using the âRuntime with JavaScriptâ option when embeding, then youâll have a few more options, using Observableâs Runtime API. If you save the (new Runtime).module(...)
call to a variable, then you can call .value("cellName")
, which returns a Promise that resolves the value of the cell cellName
after is is done computing, which is effectively the easiest way to âwait forâ a cell to be fulfilled. Hereâs an example, using the D3 Line chart notebook:
<div class="chart"></div>
<p>Credit: <a href="https://observablehq.com/@d3/line-chart">Line Chart by D3</a></p>
<script type="module">
import {Runtime, Inspector} from "https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js";
import define from "https://api.observablehq.com/@d3/line-chart.js?v=3";
const notebookModule = (new Runtime).module(define, name => {
if (name === "chart") return Inspector.into(".chart")();
});
notebookModule.value("chart").then(chart => {
// Then runs when the "chart"` cell has been fulfilled and is already mounted to the DOM
console.log(chart);
})
</script>
See an example here: https://observablehq.com/d/f667c89002a018ad
(I made this snippet by going to the D3 Line chart notebook, selecting âEmbed cellâ on the chart cell, then chose âRuntime with JavaScriptâ, then copied the code and added the const notebookModule =
line and the notebookModule.value("chart").then(...)
lines.)
And for completeness, in the eyes of the Observable runtime, every cell is in 1 of 3 different states: pending
, fulfilled
, and rejected
. All cells start off as pending
(before execution), and cells either become fulfilled
or rejected
depending on if the cellâs code (and all the cellâs ancestors) succeeded or failed. If you really wanted to know when every single cell becomes âfulfilledâ, you can take a look at the 2nd parameter in .module()
, the observers
parameter, which offers a way to âhook intoâ the state changes of every cell of a given notebook. By default, it uses the Observable notebook Inspector (as seen in the above example), but if you wanted to both 1) have a way to hook into the state changes of every cell in your notebook, and 2) still use the same functionality offered by the official Inspector, you could do something like this:
(new Runtime).module(define, name => {
const i = new Inspector(document.body.appendChild(document.createElement("div")));
return {
pending() {
console.log(name, "pending");
i.pending();
},
fulfilled(value) {
console.log(name, "fulfilled");
i.fulfilled(value);
},
rejected(error) {
console.log(name, "rejected");
i.rejected(error);
},
};
})
Itâs probably overkill for any of what youâre doing, but always an option!