You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SAP UI5中Promise失效导致应用偶发崩溃问题求助

解决SAP UI5中Promise未生效导致Formatter找不到数据的问题

我来帮你分析并解决这个问题——你遇到的核心问题是Promise没有按预期控制数据加载与视图渲染的顺序,加上代码里的几处小错误,导致偶发的formatter报错和页面冻结。

先理清楚你遇到的现象:

  • 异常情况下,后续视图的formatter在OData请求完成前就触发了,找不到数据报错
  • 正常情况下,所有数据加载完成后才会执行formatter,页面正常显示

问题根源分析

  1. 明显的回调错误:你自己也发现了,/External_OrganizationsSet的success回调误用了oModelRefDataDeferred.resolve(),而不是对应的oModelExtOrgDataDeferred。这会导致这个Deferred永远不会被resolve,jQuery.when会一直处于等待状态,后续视图加载时数据自然没准备好。
  2. Deferred作用域问题:你把Deferred变量定义在sap.ui.define的回调外层,这会让它们变成模块级的全局变量。如果应用有路由导航导致视图重建,这些Deferred可能已经处于resolved状态,不会重新触发,逻辑直接混乱。
  3. Promise使用不规范:jQuery.when(...).done().then(...)的写法冗余,而且没有处理错误场景——如果某个OData请求失败,对应的Deferred没被reject,jQuery.when会一直pending,直接卡死应用。
  4. 视图加载时机未受控:即使Promise正常工作,你也没控制后续视图(比如Worklist)的加载时机,它可能在数据准备好之前就渲染了,导致formatter提前执行。

一步步解决问题

1. 先修复回调的低级错误

把/External_OrganizationsSet的success回调改成正确的Deferred,同时错误时也要reject,避免Promise一直挂着:

sPath = "/External_OrganizationsSet";
oModel.read(sPath, {
  success: function(oData, oResponse) {
    oModelExtOrgDataDeferred.resolve(); // 这里换成正确的Deferred
  },
  error: function(oError) {
    jQuery.sap.log.error("Error loading External Organizations", oError);
    oModelExtOrgDataDeferred.reject(oError); // 错误时必须reject
  }
});

2. 调整Deferred的作用域

把所有Deferred变量移到onInit函数内部,避免模块级变量带来的重复初始化问题:

onInit: function () {
  // 把Deferred移到这里,每次初始化控制器都会创建新的Deferred
  var oModelTypeDataDeferred = jQuery.Deferred();
  var oModelStatusDataDeferred = jQuery.Deferred();
  var oModelRoleDataDeferred = jQuery.Deferred();
  var oModelRefDataDeferred = jQuery.Deferred();
  var oModelExtOrgDataDeferred = jQuery.Deferred();
  var oModelInvolvementDataDeferred = jQuery.Deferred();

  // 剩余代码...
}

3. 用Promise控制视图渲染时机

为了确保formatter只在数据就绪后执行,我们可以给appView模型加一个dataReady标志,formatter先判断这个标志再执行逻辑:

首先在readyToGo里设置标志:

var readyToGo = function() {
  jQuery.sap.log.error("Ready!");
  // 告诉所有视图数据已经准备好
  this.getModel("appView").setProperty("/dataReady", true);
  fnSetAppNotBusy();
};

然后在formatter里做判断:

// 示例formatter函数
sap.ui.define([], function() {
  return {
    formatAgreementType: function(sType) {
      var oAppModel = this.getView().getModel("appView");
      // 数据未就绪时返回空或者占位符,避免报错
      if (!oAppModel.getProperty("/dataReady")) {
        return "";
      }
      // 正常的格式化逻辑
      var oF4Model = this.getView().getModel("f4");
      var aTypes = oF4Model.getProperty("/Agreement_TypesSet");
      // ... 你的格式化代码
    }
  };
});

4. 优化Promise封装(可选但推荐)

手动创建Deferred比较繁琐,我们可以把OData read封装成原生Promise,代码更简洁易维护:

// 封装OData read为Promise函数
var readOData = function(oModel, sPath) {
  return new Promise(function(resolve, reject) {
    oModel.read(sPath, {
      success: resolve,
      error: function(oError) {
        jQuery.sap.log.error(`Failed to load ${sPath}`, oError);
        reject(oError);
      }
    });
  });
};

// 替换原来的多个read请求和jQuery.when
oModel.metadataLoaded().then(function(){
  Promise.all([
    readOData(oModel, "/Agreement_TypesSet"),
    readOData(oModel, "/Agreement_StatusesSet"),
    readOData(oModel, "/Role_TypesSet"),
    readOData(oModel, "/Reference_TypesSet"),
    readOData(oModel, "/External_OrganizationsSet"),
    readOData(oModel, "/Involvement_TypesSet")
  ]).then(function(results) {
    // 可选:把返回的数据直接存入f4模型,方便formatter获取
    oModel.setProperty("/Agreement_TypesSet", results[0]);
    oModel.setProperty("/Agreement_StatusesSet", results[1]);
    oModel.setProperty("/Role_TypesSet", results[2]);
    oModel.setProperty("/Reference_TypesSet", results[3]);
    oModel.setProperty("/External_OrganizationsSet", results[4]);
    oModel.setProperty("/Involvement_TypesSet", results[5]);

    readyToGo.call(this);
  }.bind(this)).catch(function(oError) {
    jQuery.sap.log.error("Data loading failed", oError);
    fnSetAppNotBusy();
  });
}.bind(this));

5. 确保路由配置正确(补充)

检查manifest.json里的路由配置,确保初始路由(Worklist)是异步加载的,并且根视图的初始化完成后才会渲染:

"sap.ui5": {
  "routing": {
    "config": {
      "routerClass": "sap.m.routing.Router",
      "viewType": "XML",
      "viewPath": "kristal.apps.agreements.view",
      "controlId": "app",
      "controlAggregation": "pages",
      "async": true // 确保视图异步加载
    },
    "routes": [
      {
        "pattern": "",
        "name": "worklist",
        "target": "worklist"
      }
    ],
    "targets": {
      "worklist": {
        "viewName": "Worklist",
        "viewLevel": 1
      }
    }
  }
}

最终修正后的完整控制器代码

sap.ui.define([
  "kristal/apps/agreements/controller/BaseController",
  "sap/ui/model/json/JSONModel"
], function (BaseController, JSONModel) {
  "use strict";

  return BaseController.extend("kristal.apps.agreements.controller.App", {
    onInit: function () {
      var readyToGo = function() {
        jQuery.sap.log.error("Ready!");
        this.getModel("appView").setProperty("/dataReady", true);
        fnSetAppNotBusy();
      };

      var oViewModel = new JSONModel({
        busy: true,
        delay: 0,
        dataReady: false
      });
      this.setModel(oViewModel, "appView");

      var iOriginalBusyDelay = this.getView().getBusyIndicatorDelay();
      var fnSetAppNotBusy = function() {
        oViewModel.setProperty("/busy", false);
        oViewModel.setProperty("/delay", iOriginalBusyDelay);
      };

      this.getOwnerComponent().getModel().metadataLoaded().then(fnSetAppNotBusy);

      var oModel = this.getOwnerComponent().getModel("f4");

      // 封装OData read为Promise
      var readOData = function(oModel, sPath) {
        return new Promise(function(resolve, reject) {
          oModel.read(sPath, {
            success: resolve,
            error: function(oError) {
              jQuery.sap.log.error(`Failed to load ${sPath}`, oError);
              reject(oError);
            }
          });
        });
      };

      oModel.metadataLoaded().then(function(){
        Promise.all([
          readOData(oModel, "/Agreement_TypesSet"),
          readOData(oModel, "/Agreement_StatusesSet"),
          readOData(oModel, "/Role_TypesSet"),
          readOData(oModel, "/Reference_TypesSet"),
          readOData(oModel, "/External_OrganizationsSet"),
          readOData(oModel, "/Involvement_TypesSet")
        ]).then(function(results) {
          // 将数据存入模型
          oModel.setProperty("/Agreement_TypesSet", results[0]);
          oModel.setProperty("/Agreement_StatusesSet", results[1]);
          oModel.setProperty("/Role_TypesSet", results[2]);
          oModel.setProperty("/Reference_TypesSet", results[3]);
          oModel.setProperty("/External_OrganizationsSet", results[4]);
          oModel.setProperty("/Involvement_TypesSet", results[5]);

          readyToGo.call(this);
        }.bind(this)).catch(function(oError) {
          jQuery.sap.log.error("Data loading failed", oError);
          fnSetAppNotBusy();
        });
      }.bind(this));
    }
    // 其他控制器方法...
  });
});

内容的提问来源于stack exchange,提问作者Greg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:52:44