I'm staring my UI5 with the following bootstrapping in index.js
:
sap.ui.define([
"sap/m/Shell",
"sap/ui/core/ComponentContainer"
], (Core, Shell, ComponentContainer) => {
"use strict";
new Shell("", {
app: new ComponentContainer("", {
height: "100%",
name: "webapp"
}),
appWidthLimited: false
}).placeAt("content");
});
According to the UI5 documentation:
Static dependencies are loaded in the dependency declaration array of the
sap.ui.define
call. These dependencies are always loaded in advance before executing the defined module.
Do I understand it correctly that in such case the modules will be always loaded in a synchronous way and there is no real way to avoid the «Synchronous XMLHttpRequest on the main thread» warning?
Can I, perhaps, just wrap new sap.m.Shell(sId?, mSettings?)
with async
/await
?
I've checked the loading with ?sap-ui-xx-nosync=warn
and got the following results:
For some reason i18n/i18n_en.properties
is loaded synchronously. The only place where I'm accessing the i18n
is:
const oBundle = myI18nModel.getResourceBundle();
But following the documentation, I could not grasp why myI18nModel.getResourceBundle()
leads to the synchronous loading.
After a deep exploration of no sync XHR sample, I found out the reason for the sync XHR
warning. That was "description": "{{appDescription}}"
and "title": "{{appTitle}}"
in manifest.json
which is explicitly noted:
"title": "Here the i18n bundles can't be loaded with 'async: true' yet!",
"description": "Same here. Hence, no 'i18n' in this 'sap.app' section!"
After replacing it with a static values the warning is gone.
Historically, UI5 1.x had been offering APIs based on synchronous XHRs. In order to avoid breaking existing apps and to support backward compatibility of new minor and patch releases, UI5 1.x must keep the current APIs. Legacy applications or outdated documentation topics might still refer to:
sap.ui.core.ComponentContainer
, sap.ui.component
, sap.ui.*view
, sap.ui.controller
, sap.ui.core.Core#loadLibrary
, etc..sap.ui.*fragment
, jQuery.sap.require
, jQuery.sap.sjax
, sap.ui.requireSync
, sap.ui.core.Core#createComponent
, sap.ushell.Container.getService
, etc..In either case, the good news is that it is still possible to fully avoid sync XHRs today with UI5 1.x.
With UI5 2.x, no public APIs will initiate sync XHRs.
In order to get rid of sync XHRs in UI5:
Do not use deprecated APIs such as those mentioned above. Which API to use instead or how to use it asynchronously is documented in the API Reference. Deprecated APIs will be removed in the next major UI5 version (2.x). UI5 linter might help to detect deprecated APIs in your project.
Make sure that dependent libraries and components are preloaded before accessing modules from the respective preload bundle. For example, if the sap.f.FlexibleColumnLayout
control is part of the root view, add "sap.f": {}
to the manifest.json
section /sap.ui5/dependencies/libs
. Do not set { "lazy": true }
since the framework will then simply skip fetching the preload bundle, in which the application must preload it instead.
Make sure that the application code really doesn't initiate any sync XHR by itself; e.g. passing async: false
to jQuery.ajax
or a falsy 3rd argument to XMLHttpRequest#open
.
Follow the documented guidelines:
Particularly when bootstrapping, use the data-sap-ui-async="true"
option, and, if the app has a Component.js
, use the declarative sap/ui/core/ComponentSupport
module in data-sap-ui-onInit
instead of instantiating sap.ui.core.ComponentContainer
manually. For example:
<head>
<!-- ... -->
<script id="sap-ui-bootstrap" src="..."
data-sap-ui-async="true"
data-sap-ui-onInit="module:sap/ui/core/ComponentSupport"
data-sap-ui-resourceRoots='{ "my.demo": "./" }'
data-...="..."
></script>
</head>
<body id="content" class="sapUiBody">
<div data-sap-ui-component
data-id="rootComponentContainer"
data-name="my.demo"
data-settings='{ "id": "myRootComponent" }'
data-...="..."
></div>
</body>
This automatically creates a ComponentContainer
, puts it in the DOM, and loads the corresponding Component.js
and manifest.json
asynchronously altogether while avoiding inline script or a separate bootstrap script at the same time.
In the application's Component.js
definition, additionally, implement the "sap.ui.core.IAsyncContentCreation"
marker interface to allow creating the component's content fully asynchronously (in addition to stricter error handling).
// In Component.js
return UIComponent.extend("my.demo.Component", {
metadata: {
interfaces: [ "sap.ui.core.IAsyncContentCreation" ],
manifest: "json",
},
// ...
});
For UI5 1.x: run the app with the xx-nosync
bootstrap option. For example, in the URL:
https://<host>/my/awesome/app/?sap-ui-xx-nosync=warn
UI5 will then log "loading … with sync XHR" or "[nosync] loading module '…'" in the browser console for each file fetched synchronously (Ignore other [nosync] messages). For example:
Standalone apps using sap.ui.model.odata.v2.ODataModel
with older UI5 versions might require sap/ui/thirdparty/datajs
synchronously (C.f. issue #3134). In that case, add the module to the sap-ui-modules
bootstrap config:
<script id="sap-ui-bootstrap" src="..."
data-sap-ui-async="true"
data-sap-ui-onInit="module:sap/ui/core/ComponentSupport"
data-sap-ui-modules="sap/ui/thirdparty/datajs"
...
></script>
In an FLP scenario, the datajs
module should be already preloaded by the launchpad.
If a ResourceModel
is created manually in JS, make sure to enable the async
flag there:
const i18nModel = new ResourceModel({ // sap/ui/model/resource/ResourceModel
bundleName: "demo.i18n.i18n",
supportedLocales: [""],
fallbackLocale: "",
async: true, // <--
});
const resourceBundle = await i18nModel.getResourceBundle();
this.getOwnerComponent().setModel(i18nModel, "i18n");
resourceBundle.getText("...", [/*...*/]); // sap/base/i18n/ResourceBundle
Check the API reference of the reported APIs. Most of the times, they're either deprecated or they come with an async
flag set to false
by default.
When requesting UI5 theming parameters via sap.ui.core.theming.Parameters.get
, use the asynchronous variant as mentioned in the API description. If there are standard UI5 controls requesting theming parameters too early - causing a sync XHR - create an issue in OpenUI5 GitHub.
See also other best practices for UI5 developers.
If the app is supposed to start from SAP Fiori launchpad (FLP), follow How to make UI5 content compatible with the FLP setting "Asynchronous Module Loading" and CSP?
There are, however, still some APIs left which have no asynchronous replacements yet such as:
sap/ui/core/LocaleData
(Used in e.g. Calendar or DatePicker)."{{...}}"
-syntax in manifest.json
resulting in sync XHRs. (Should not be an issue if the *.properties
files are included in the generated application bundle Component-preload.js
). → ✅ Fixed with ad5cf65
.v2.ODataModel
requiring datajs.js
synchronously if created with preload: true
in manifest.json
. → ✅ Fixed with 337b176
.To answer the question: sap.ui.define
and sap.ui.require
do load dependencies asynchronously if the data-sap-ui-async
config is set to true
which can replace data-sap-ui-preload="async"
. Also, make sure to add dependent libs to the manifest.json
in order to prevent a major performance bottleneck.
Wrapping a synchronous content request with async-await or Promise won't help. Better look for asynchronous alternatives. For example, the ComponentContainer
has an async
flag in the constructor settings which, if enabled, fetches the Component.js
and manifest.json
files asynchronously:
new ComponentContainer({ // required from "sap/ui/core/ComponentContainer"
height: "100%",
name: "my.demo",
async: true, // or
manifest: true, // sets the `async` option automatically to true
// ...
});