get(objId, callback = null) {
    // If there is a callback, then the get can be async and the object is
    // not required to be resolved right now.
    if (callback) {
      const obj = this.#ensureObj(objId);
      obj.promise.then(() => callback(obj.data));
      return null;
    }
    // If there isn't a callback, the user expects to get the resolved data
    // directly.
    const obj = this.#objs[objId];
    // If there isn't an object yet or the object isn't resolved, then the
    // data isn't ready yet!
    if (!obj || obj.data === INITIAL_DATA) {
      throw new Error(`Requesting object that isn't resolved yet ${objId}.`);
    }
    return obj.data;
  }
If called *without* callback, this returns the data of `objId` but the
object needs to be resolved. If it isn't, this method throws.

If called *with* a callback, the callback is called with the data of the
object once the object is resolved. That means, if you call this method
and the object is already resolved, the callback gets called right away.

@param {string} objId
@param {function} [callback]
@returns {any}