In DESITE BIM Version 3.0 and upwards, several adjustments are necessary due to an update of the underlying web technology.
These adjustments affect all custom webforms and details widgets. All scripts, f. e. object scripts, remain unaffected.
Asynchronous API calls
The main change within the webforms is that API calls are no longer handled synchronously, but instead are handled asynchronously. In JavaScript a function must be defined with the keyword “async” in front. An asynchronous function will be executed in parallel with the calling function, so it is no longer guaranteed that all operations are handled in consecutive order. The next segment will prepare the user for handling asynchronous functions and their return parameters.
1. Completion time is not relevant:
The simplest case is when the time of completion is not relevant for the calling function, in that case the asynchronous call can be handled just like before. It must be considered that e.g., an asynchronous call to “showAll” with a follow-up call to “getVisibleElements” where the first would manipulate the state of the second could lead to unexpected behavior.
function run()
{
desiteAPI.showAll(true, 'geometry');
desiteAPI.showAll(true, 'documents');
}
2. Wait for one/multiple promises:
It is possible to handle asynchronous function calls in a synchronous way. JavaScript introduces the so-called promise object. It is returned when an asynchronous function is called and replaces the regular return value. A promise represents a running operation, in conjunction with the await-operator, the user can now wait for a function to complete. The usage of the await-operator itself is only possible in an asynchronous function, otherwise the execution would stall the running process. That’s why the calling function must be declared with the “async” keyword.
async function waitForOnePromise()
{
ids = await desiteAPI.getVisibleElements();
for(var i = 0; i < ids.length; i++)
{
var obj = await desiteAPI.getAsJSON(ids[i]);
/* or explicit */
var promise = desiteAPI.getAsJSON(ids[i]);
var obj = await promise;
}
}
Besides waiting for a single promise, it is also possible to wait for multiple promises at once. This is beneficial and will reduce the overall time of the operation. The user has to collect all promises in an array and afterwards pass it on to the await-operator. The result of that operation will be a new array with the original function return values.
async function waitForAllPromises()
{
ids = await desiteAPI.getVisibleElements();
var promises = [];
for(var i = 0; i < ids.length; i++)
{
promises.push(desiteAPI.getAsJSON(ids[i]));
}
var objs = await Promise.all(promises);
}
3. Callbacks:
Another option to handle asynchronous calls are callback functions. The callback function will be passed into an asynchronous function as an additional parameter and will be called on function completion. The callback functions will pass the original return value through its parameter list. The calling function doesn’t have to wait for the asynchronous call and can continue with its work, the “async” keyword is not necessary.
function runWithCallback()
{
desiteAPI.getVisibleElements(function(ids)
{
for(var i = 0; i < ids.length; i++)
{
desiteAPI.getAsJSON(ids[i], function(obj)
{
/* do something with object */
});
}
});
}
In summary, it is always advantageous to NOT wait for single API calls and use callback functions instead. Alternatively, if an operation depends on former results and the user wants to handle it in a synchronous way, waiting for as many operations as possible at once is preferred and will reduce the overall time of the process.
There is a performance test script in the download area for reference (Benchmark.zip). It will show execution timings for the different options available. A simple test that creates 100 objects shows that waiting for every promise will take about double the time compared to waiting for all promises and callbacks. These observations should help to give a broad estimate and are not universally applicable.
Entry point
Up to this point the entry point for initializing data was the ‘load’ event of the HTML body. This is no longer an option; the new API is not initialized at that point. On successful initialization a custom “desiteload“ event will be sent that can be handled with an event listener.
addEventListener('desiteload', function(event)
{
/* do your initialization here */
});
API functions with an internal state
Extra precautions have to be taken when using API functions with an internal state.
For example, when you open a file via openFile(), write multiple chunks of data via writeToFile(), and then close the file via closeFile(). Since the calls must be made in exactly this sequence, WebForm implementers have to make sure that they are not executed asynchronously, and therefore out of order.
Another common and important case are the iterator functions, when, for example, you define an object subset via itByFilter() and then iterate over it using itHasNext() and itNext(). These functions are now deprecated. Instead, you should use filterByProperty(), which does not store the filtered object list internally. The list is passed in as a parameter and given back as return value.
Please also check the API documentation for more info.