javascriptasync-awaitsapui5sap-web-ideui5-tooling

Using async/await for promise-based APIs in UI5


The official UI5 documentation proposes to load a Fragment using promises:

Fragment.load({
    controller: oFragmentController,
        id: oView.getId(),
        name: "webapp.view.HelloDialog"
    }).then((oDialog) => {

    oView.addDependent(oDialog);

    oDialog.open();

});

I personally would prefer to use async/await instead:

const oDialog = await Fragment.load({
    controller: oFragmentController,
    id: oView.getId(),
    name: "webapp.view.HelloDialog"
});

oView.addDependent(oDialog);

oDialog.open();

How save would be such code refactoring and transition from .then-promises to async/await in UI5? Can it lead to some issues?


Solution

  • Generally, asyncawait is considered syntactic sugar for Promises. However, one key distinction is that async functions always return a Promise. From MDN:

    An async function declaration creates an AsyncFunction object. Each time when an async function is called, it returns a new Promise which will be resolved with the value returned by the async function, or rejected with an exception uncaught within the async function.

    In development with UI5

    When working with UI5 Tooling or SAP Fiori tools, there are some limitations to be aware of. Refer to the sections "Asynchronous Factory Function" and "Event Handler Registration" in the topic ECMAScript Support. With the documented constraints in mind, async-await can be used safely.

    Given <Dialog id="helloDialog"> in the fragment definition, and this being the current controller instance, creating the fragment would then look like this:

    With this.loadFragment (UI5 1.93+)

    // this === Controller instance
    onOpenDialog: async function() {
      const dialog = this.byId("helloDialog") || await this.loadFragment({
        name: "webapp.view.HelloDialog"
      });
      dialog.open();
    },
    

    With Fragment.load (UI5 1.58+)

    // this === Controller instance
    // Fragment required from "sap/ui/core/Fragment"
    onOpenDialog: async function() {
      let dialog = this.byId("helloDialog");
      if (!dialog) {
        dialog = await this.getOwnerComponent().runAsOwner(() => Fragment.load({
          id: this.getView().getId(),
          name: "webapp.view.HelloDialog",
          controller: this,
        }));
        this.getView().addDependent(dialog);
      }
      dialog.open();
    },
    

    The legacy factory function sap.ui.*fragment is deprecated as it creates fragments synchronously. Use one of the above APIs instead.

    That being said, if the app is targeting abandoned browsers (e.g. Internet Explorer) or being developed in an IDE that relies on outdated tooling (e.g. SAP Web IDE), asyncawait should be avoided completely. In some environments, Promise or then-able functions work only thanks to a polyfill that gets applied if the Promise is not already natively or fully supported. *


    * IE11 is no longer supported. The polyfill is also removed from UI5 accordingly.
    SAP Web IDE is obsolete. Migrate your projects to Visual Studio Code or SAP Business Application Studio (BAS), and use SAP Fiori tools / UI5 Tooling accordingly.