Skip to Content
Technical Articles

UI5ers Buzz #41: Best practices for async loading in UI5

Why so synchronous?

Today we have some bad news for you: We had to remove cool features like synchronous requests and eval statements. Sad.

Ok, jokes aside, it was time for UI5 to evolve and provide asynchronous paths for all our framework features. Though, we are not done yet and still have to solve some synchronous parts within older features, but we want to give you a short update of our progress so far.

Previously on UI5ers buzz (here and here) we gave you some initial guidance on how to ready your application for fully asynchronous usage. Dependency management being the main point here (see Best Practices for Loading Modules).

Now, in this blog post we want to give you an overview of some of the main starting points for building a fully asynchronous application and leveraging current performance improvements.

The UI5 Bootstrapping

The very first thing we need to tackle is a variant of the UI5 bootstrap. It comes with a new setting data-sap-ui-async which you definitely want to set to “true”. This leverages the browsers capabilities to execute multiple requests in parallel, without blocking the UI thread. Further details on the configuration options can be read in this documentation.

The library-dependencies can be declared via the data-sap-ui-libs data-attribute in case you don’t use a component. This makes sure that all your relevant libraries are loaded asynchronously before any app logic runs. Also check out this guide for more.

In case you are using a component, we recommend you to declare your libraries inside the manifest.json. A component encapsulates a specific part of your application and in some cases you may have multiple components. For each of your components you have a manifest.json in which you can define your dependencies. This makes sure that for each component only the relevant libraries are loaded. Check out the following guide for more.

The following example shows you a very simple bootstrap with an initial module “my/app/main” set via the new data-sap-ui-onInit setting. This module will be executed after the UI5 core was booted and can be used as the entry point for your application. Inside this module you can require other modules.

UI5 Bootstrap
<script id="sap-ui-bootstrap" src="/resources/sap-ui-core.js"
    [...]
    data-sap-ui-async="true"
    data-sap-ui-onInit="module:my/app/Main"
    data-sap-ui-resourceRoots="{'my.app': './'}">
    [...]
</script>

The “my/app/main” module which is declared in the previous example could look something like below:

module:my/app/Main.js
sap.ui.define(["sap/base/Log", "sap/base/strings/camelize"], function (Log, camelize) {
    "use strict";
    Log.info(camelize("camelize-my-strings")); //camelizeMyStrings
});

Well that’s pretty simple…

So alternatively to the onInit module, you can also boot UI5 components with the data-attribute data-sap-ui-onInit set to module:sap/ui/core/ComponentSupport. The ComponentSupport class provides functionality which allows you to declare your components in HTML.

All you need to do is to implement a div element with the data-sap-ui-component attribute and the data-name attribute containing the component name.

Initial component loading
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Basic Template</title>
 
    <script id="sap-ui-bootstrap"
        src="/resources/sap-ui-core.js"
        data-sap-ui-theme="sap_belize"
        data-sap-ui-libs="sap.m"
        data-sap-ui-compatVersion="edge"
        data-sap-ui-async="true"
        data-sap-ui-onInit="module:sap/ui/core/ComponentSupport"
        data-sap-ui-resourceRoots="{'my.app': './'}">
    </script>
</head>
 
<body class="sapUiBody">
    // Define your component in a declarative way inside your HTML page
    <div data-sap-ui-component data-name="my.app.Component" data-id="container"></div>
</body>
</html>

 

Async XML Views

After we have prepared the asynchronous bootstrap to startup UI5, we now have to think about how we can get some content into our app. Typically you want to use XML Views for this.

Mainly you have two ways to use an XML view, either you define it as a root view in the manifest.json of your component (see example below) or you use our new factory functions instead (again: check out our previous blog post (wink)). In both cases you can set the XML view to async = true to enable the asynchronous loading of the XML document.

Inside the manifest it will look something like this:

manifest.json
[...]
    "sap.ui5": {
        "rootView": {
            "viewName": "my.app.Main",
            "type": "XML",
            "async": true
        }
    [...]
    }
[...]

When using your own onInit module, the XML view creation could look something like below.

Here we have a dependency on the XMLView class and are creating a simple view instance. If you remember our last blog post, we have deprecated the old sap.ui.* factory functions (e.g. sap.ui.view) and introduced new fully asynchronous alternatives. We now use one of the new factories: XMLView.create().

The new factory returns a Promise which resolves with the processed view instance. In the resolve callback we then simply place the view in the DOM for rendering.

module:my/app/Main.js
sap.ui.define([“sap/ui/core/mvc/XMLView”], function (XMLView) {
    “use strict”;
 
    XMLView.create({
        viewName: “my.app.Root”
    }).then(function (oView) {
        oView.placeAt(“content”);
    });
});

 

OData Models

Beside your views you typically have your data models which take care of the data management of your application. For your OData use cases you should use the v2 or v4 OData models, since those are fully asynchronous as opposed to the deprecated v1 OData model. If you are defining your OData models in the manifest.json, you might want to look at the preload flag. Setting the preload flag to true will already send the OData model’s metadata request in parallel to the component preload.

Before enabling the preload for your OData models, make sure you use the metadata loaded Promise API instead of the Event API. Otherwise you might miss the one-time metadataLoaded event. With the model preload, the metadata document could have been loaded before the application code is executed. The Promise however, will be executed even if the metadataLoaded event was fired already.

The configuration in your manifest file will look something like this:

Manifest Model Preload
[...]
  "sap.ui5": {
    [...]
    "models": {
        "myModel": {
            "preload": true,
            [...]
        }
    }
[...]

Inside your application’s controller you can then use the OData model’s metadata loaded Promise like so:

myModel.metadataLoaded().then(function() { ... });

 

Don’t worry, be async…

In this episode we gave you a brief overview of the aspects in UI5 that run fully asynchronous and demonstrated how you can already apply these features to boost the performance of your application.

So what are the take-aways for you?

Let’s summarize this blog post into a few steps:

Step 1: Avoid stuck UIs by using the data-sap-ui-async=”true” in the bootstrap. With this configuration set, UI5 loads modules and library-preload files asynchronously, unleashing the browser’s capability to run parallel requests. Awesome. More here.

Step 2: Go ahead and take advantage of loading and processing XML views asynchronously with UI5, which adds more value to the overall performance of your application. Nice. More here.

Step 3: Don’t use the v1 OData model anymore, it’s deprecated! Make usage of the v2 or v4 OData models, since they are fully asynchronous.

Step 4: Profit!

Final Words

Please be aware that your application’s timing will be impacted by incorporating asynchronous APIs in your application. Especially older applications might run into issues when switching to newer async APIs. A very common problem is the usage of the EventBus. The EventBus relies on a fixed call order. If one part of your application publishes an Event before another part could subscribe to it, the Event is lost. In these cases you should consider using Promises instead.

In case you run into any troubles with the usage of the new asynchronous APIs, please take a look at our trouble shooting guide. This guide lists the most common issues and might help you in solving them.

 

Previous post: UI5ers Buzz #40

Author

Thorsten is a UI5 Core Developer, gaming enthusiast, minimalist, and highly addicted to High Definition Entertainment Systems. He is currently focusing on improving the UI5 code base to make it a better place for applications.
Tommy is a UI5 Core Developer, food photographer, home cinema enthusiast, and always owns the latest consumer electronic gadgets. In his daily work, he tries to leave our code base in a better shape than he has found it.

9 Comments
You must be Logged on to comment or reply to a post.
  • Thanks for the blog.

     

    I have a question regarding libraries. If I specify a library, eg sap.m in the manifest, do I still have to specify it in the views?. If so, why?

     

    Kind regards

    • Hi Timothy Muchena,

      What do you mean exactly with “specifying in views”?

      By declaring the libraries in the manifest, they are loaded asynchronously and should be available before your view is initialized.

      In e.g. XMLViews you only specify xml namespaces. Control libraries (e.g. sap.m) are mapped to xml namespaces to be accessed. This does not affect the loading of the library.

      Best regards,

      Tommy

  • Nice blog, thanks. I have a small question, in the first UI5 bootstrap example:

     data-sap-ui-onInit="module:my/app/main"

    Shouldn’t that be my/app/Main ? or doesn’t the case matter here?

  • Did you also consider using ES module syntax and allowing bundlers to manage the complexity of dependency resolution, deduping and a sync loading?

     

  • Post was helpfull for me 🙂 but stiill looking for answer(s) to following question: what is the difference between following bootstrapps:

    Bootstrapping 1:
    sap.ui.getCore().attachInit(function()
    { sap.ui.require([ "sap/m/Shell", "sap/ui/core/ComponentContainer" ], function(Shell, ComponentContainer) { // initialize the UI component new Shell({ app: new ComponentContainer({ height: "100%", async: true, name: "comp.biz" }) }).placeAt("content"); }); }); Bootstrapping 2:
    <script id="sap-ui-bootstrap" src="libs/sapui5/sap-ui-core.js" data-sap-ui-libs="sap.m" data-sap-ui-theme="sap_bluecrystal" data-sap-ui-compatVersion="edge" data-sap-ui-resourceroots='{"corp.biz": "."}' data-sap-ui-async="false" data-sap-ui-frameOptions="trusted"> </script>