22 Jan 2021

Wait for events in the Viewer

Sooner or later you will need to handle events inside the Viewer - e.g. to wait for the geometry to be loaded before using the instanceTree object.

Sometimes you might even need to wait for multiple events before continuing, e.g. GEOMETRY_LOADED_EVENT & OBJECT_TREE_CREATED_EVENT as also discussed in this blog post: Asynchronous viewer events notification 

That article contains a useful function for waiting for multiple events using the async library.
Since the Viewer code is already using Promise objects, we might as well rely on that:

function afterViewerEvents(viewer, events) {
    let promises = [];
    events.forEach(function (event) {
        promises.push(new Promise(function (resolve, reject) {
            let handler = function () {
                viewer.removeEventListener(event, handler);
                console.log(`Removed event listener for ${event}`)
                resolve();
            }
            viewer.addEventListener(event, handler);
            console.log(`Added event listener for ${event}`)
        }));
    });

    return Promise.all(promises)
}

Now we can simply await this function before doing something that relies on those events happening:

viewer.loadDocumentNode(doc, items[0], {}).then(async function (model) {
  await afterViewerEvents(
    viewer,
    [
      Autodesk.Viewing.GEOMETRY_LOADED_EVENT,
      Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT
    ]
  );

  // do something
});

The Viewer has some utility functions for events as well that you could use instead.
E.g. if you just needed to wait for the GEOMETRY_LOADED_EVENT then you could do this:

viewer.loadDocumentNode(doc, items[0], {}).then(async function (model) {   
  await Autodesk.Viewing.EventUtils.waitUntilGeometryLoaded(viewer);

  // do something
});

There is now another function available that might be even more useful: waitForLoadDone(include)
It lets you specify which model(s) you want to wait for and what parts you are interested in: geometry, property database, textures.  

In the case of multiple models, it's probably better to use this function since you can also provide which model you are waiting for. Also, if the event already happened, and the resource is available, then the Promise it returns will resolve

await viewer.waitForLoadDone({ propDb: true, geometry: true, onlyModels: [model] });

 

Related Article