Debugging promise failures outside Observable UI

I’ve been experimenting with embedding code from multiple Observable notebooks in systems outside of the main Observable UI.

This has resulted in long sessions of head-scratching while I’m trying to understand what exactly went wrong when loading a cell. I realized that debugging would be much easier if I could walk the status of all Observable variables and log corresponding rejection values.

Here’s a bit of code that does that:


function dumpRejectedVariables(runtime) {
  // Hat-tip to https://makandracards.com/makandra/46681-javascript-how-to-query-the-state-of-a-native-promise
  function promiseState(promise, callback) {
    // Symbols and RegExps are never content-equal
    var uniqueValue = window['Symbol'] ? Symbol('unique') : /unique/

    function notifyPendingOrResolved(value) {
      if (value === uniqueValue) {
        return callback('pending')
      } else {
        return callback('fulfilled')
      }
    }

    function notifyRejected(reason) {
      return callback('rejected', reason)
    }

    var race = [promise, Promise.resolve(uniqueValue)]
    Promise.race(race).then(notifyPendingOrResolved, notifyRejected)
  }

  for (const v of runtime._variables) {
    promiseState(v._promise, (state, val) => {
      if (state === 'rejected') {
        console.log(`[rejected] ${v._name} = ${val}`)
      }
    })
  }
}

At some point I will probably add topological sorting and better formatting to highlight what the root cause of the failure is.

Hope others find this useful!

2 Likes

That’s a handy trick!

I’d love to hear a little more about your difficulties in getting your notebooks integrated into external systems.

What exactly is making it hard to load specific cells? Is there a common pattern of error (like needing to remember to include some external dependency), or is it different every time?

Is the difficulty debugging a matter of failing cells failing invisibly, unless you explicitly log them — something that could perhaps be tackled by trying a blanket Inspector.into(...), so that you can see the entire notebook?

One barrier to debugging is that all this is in node.js, not the browser :slight_smile: Does Inspector.into(...) work on the command line?

Even when attaching chrome dev tools using --inspect-brk, it was pretty hard to understand why a particular cell never resolved. Eventually I realized it was because things were waiting on a require(...).

Once I addressed this, I realized that debugging rejections of transitive inputs was also … challenging.

Using the debugger to walk the the tree of variable inputs got pretty tedious so I decided it to automate it with the code above.

2 Likes

Ah yes, that’s a completely different story.

Do you think it would help if we offered a parameter, when creating a new Runtime(), to start it in debug mode, logging all rejected cell evaluations as errors to the console?

1 Like

A debug mode would be awesome!

That was the first thing I tried to search for before I started wandering the heap looking for clues :slight_smile: