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!