异步调用函数的角度问题

时间:2015-09-04 20:32:16

标签: angularjs angularjs-directive

我正在使用Angular directive加载来自localStorage的JSON数据。

使用localStorage.getItem(id)可以轻松完成此操作,但现在我正在尝试通过API调用使其工作(后者又从数据库中提取)。

我有Angular工厂代码正常工作,http请求正常工作,但在指令代码中有一个_handleAsyncLoad()函数,它正在抛弃我。换句话说,我正在尝试使用从API层返回的serialized对象的内置承诺。

ex /我已经编写了新的匿名函数_getItemFromAPI:,但不确定我是否需要使用_handleAsyncLoad:函数。如果没有,那么确保我的serialized对象在返回之前填充数据的最佳方法是什么。

angular.module('ui.dashboard')
  .factory('DashboardState', ['$log', '$q', 'dashboardcontext', '$rootScope', function ($log, $q, dashboardcontext, $rootScope) {
      function DashboardState(storage, id, hash, widgetDefinitions, stringify) {
          this.storage = storage;
          this.id = id;
          this.hash = hash;
          this.widgetDefinitions = widgetDefinitions;
          this.stringify = stringify;
      }

DashboardState.prototype = {
        
load: function (dashboardId) {

      if (!this.storage) {
	  return null;
      }

      var serialized;

      // fetch dashboard layout from storage
      if (dashboardId != null && dashboardId != undefined) {
	  //serialized = this.storage.getItem(dashboardId);	// OLDER, SIMPLER WAY 
	  
	  serialized = this._getItemFromAPI($rootScope);	// NEW WAY, PULL DATA VIA API !	  
      }
      else {
	  // revert to original line; see dashboardOptions to main-controller
	  serialized = this.storage.getItem(this.id);
      }

      if (serialized) {
	  // check for promise
	  if (angular.isObject(serialized)) {  
	      return this._handleAsyncLoad(serialized);
	  }
	  // otherwise handle synchronous load
	  return this._handleSyncLoad(serialized);
      } else {
	  return null;
      }
  },         

  _getItemFromAPI: function ($rootscope) {
      // SERVER-SIDE API CALL TO PERSIST DASHBOARD TO STORAGE - 09/03/2015 BM:
      var sid = $rootScope.rageSessionVars.sessionID;
      var userid = $rootScope.rageSessionVars.userID;
      var dashboardId = this.id;

      dashboardcontext.getDashboardImage(sid, userid, dashboardId).then(function (data) {
	  if (data.status == "FAIL") {
	      window.alert("Failed to retrieve dashboard. " + data.messages);
	      return false;
	  }
	  else {                      
	      return data;
	  }
      });
      return new Promise(function (resolve, reject) { });
  },

  _handleSyncLoad: function (serialized) {

      var deserialized, result = [];

      if (!serialized) {
	  return null;
      }

      if (this.stringify) {
	  try { // to deserialize the string

	      deserialized = JSON.parse(serialized);

	  } catch (e) {

	      // bad JSON, log a warning and return
	      $log.warn('Serialized dashboard state was malformed and could not be parsed: ', serialized);
	      return null;

	  }
      }
      else {
	  deserialized = serialized;
      }     

      // Cache widgets
      var savedWidgetDefs = deserialized.widgets;      

      return result;
  },

  _handleAsyncLoad: function (promise) {
      var self = this;
      var deferred = $q.defer();
      promise.then(
	// success
	function (res) {
	    var result = self._handleSyncLoad(res);
	    if (result) {
		deferred.resolve(result);
	    } else {
		deferred.reject(result);
	    }
	},
	// failure
	function (res) {
	    deferred.reject(res);
	}
      );

      return deferred.promise;
  }
 };
      
 return DashboardState;
 }]);

databoardcontext工厂代码:

function getDashboardImage(sid, userid, id) {
    var rageVars = $rootScope.rageSessionVars;
    var url = "http://" + rageVars.domainName + ":" + rageVars.port + "/api/dashboards";
    var sid = rageVars.sessionID;
    var apiCall = "getDashboardImage";
    var cssClass = "html";
    var req = {
	method: 'POST',
	url: url,
	headers: {
	    'Content-Type': 'application/json', // application/x-www-form-urlencoded
	},
	data: { sid: sid, apiCall: apiCall, userid: userid, id: id }
    };
    var deferred = $q.defer();
    deferred.notify("Retrieving dashboard image...");

    $http(req).success(function (data, status, headers, config) {

	deferred.resolve(data);

    }).error(function (data, status, headers, config) {

	console.log('Error retrieving dashboard ');
	deferred.resolve();
    });

    return deferred.promise;
}

******** UPDATE 2015年9月8日下午2:55:感谢提供答案的绅士,我发布了一些更新的代码来展示现在正在运行的内容。 ********

angular.module('ui.dashboard')
.factory('DashboardState', ['$log', '$q', 'dashboardcontext', '$rootScope', function ($log, $q, dashboardcontext, $rootScope) {

var that = this;       // *** CREATED NEW OBJECT HERE. REASSIGN BELOW IN load: FUNCTION ***

function DashboardState(storage, id, hash, widgetDefinitions, stringify) {
  this.storage = storage;
  this.id = id;
  this.hash = hash;
  this.widgetDefinitions = widgetDefinitions;
  this.stringify = stringify;
}

DashboardState.prototype = {
  save: function (widgets) {
     /// SAVE CODE OMITTED FOR BREVITY
  },
  
  load: function (dashboardId) {
      
      var useLocalStorage = false;     // retrieve from localStorage or via API layer - 09/04/2015 BM:

      var serialized;

      if (useLocalStorage) {           // retrieve dashboard layout from localStorage
	  if (!this.storage) {
	      return null;
	  }
	  if (dashboardId != null && dashboardId != undefined) {
	      serialized = this.storage.getItem(dashboardId);

	      // save the current dashboard id for next load
	      this.storage.setItem("defaultDashboardId", dashboardId);
	  }
      }
      else {	  
	  
	  if (dashboardId != null && dashboardId != undefined) {

	      this.storage.setItem("defaultDashboardId", dashboardId);
	      
	      that = this;	// **** VERY IMPORTANT TO REASSIGN ***
	      
// *** RETURN IS VERY IMPORTANT HERE, AS WELL AS THE then() SECTION ***
	      return this._getItemFromAPI($rootScope).then(function (data) {                           		 
      		  
		  return that._handleSyncLoad(data, true);  // *** that. IS NOW AVAILABLE ON THE SCOPE ***

	       });                      
	  }
	  else {
	      // revert to original line; see dashboardOptions to main-controller
	      serialized = this.storage.getItem(this.id);
	  }                  
      }

      if (serialized) {
	  // check for promise
	 if (angular.isObject(serialized)) {    
	      return this._handleAsyncLoad(serialized);
	  }
	  // otherwise handle synchronous load
	  return this._handleSyncLoad(serialized);
      } else {
	  return null;
      }

  },         

  _getItemFromAPI: function ($rootscope) {
      // SERVER-SIDE API CALL TO PERSIST DASHBOARD TO STORAGE - 09/03/2015 BM:
      var sid = $rootScope.rageSessionVars.sessionID;
      var userid = $rootScope.rageSessionVars.userID;
      var dashboardId = this.id;

      return dashboardcontext.getDashboardImage(sid, userid, dashboardId).then(function (data) {
	  return data.data[0];
      });            
  },

  _handleSyncLoad: function (serialized, isParsed) {
      // @serialized {JSON} - parsed or unparsed Json object 
      // @isParsed {Boolean} - false if loaded from localStorage.getItem(); true if loaded from API, Json string already parsed.

      var deserialized, result = [];

      if (!serialized) {
	  return null;
      }

      if (isParsed) {    // JSON already deserialzed in load: above; see _getItemFromAPI().then data object - 09/04/2015 BM:

	deserialized = serialized;               

      }
      else {
	  if (this.stringify) {
	      try { // to deserialize the string

		  deserialized = JSON.parse(serialized);

	      } catch (e) {

		  // bad JSON, log a warning and return
		  $log.warn('Serialized dashboard state was malformed and could not be parsed: ', serialized);
		  return null;

	      }
	  }
	  else {
	      deserialized = serialized;
	  }
      }

      // check hash against current hash
      if (deserialized.hash !== this.hash) {

	  $log.info('Serialized dashboard from storage was stale (old hash: ' + deserialized.hash + ', new hash: ' + this.hash + ')');
	  this.storage.removeItem(this.id);
	  return null;

      }

      // Cache widgets
      var savedWidgetDefs = deserialized.widgets;

      // instantiate widgets from stored data
      for (var i = 0; i < savedWidgetDefs.length; i++) {

	  // deserialized object
	  var savedWidgetDef = savedWidgetDefs[i];

	  // widget definition to use
	  var widgetDefinition = this.widgetDefinitions.getByName(savedWidgetDef.name);

	  // check for no widget
	  if (!widgetDefinition) {
	      // no widget definition found, remove and return false
	      $log.warn('Widget with name "' + savedWidgetDef.name + '" was not found in given widget definition objects');
	      continue;
	  }

	  // check widget-specific storageHash
	  if (widgetDefinition.hasOwnProperty('storageHash') && widgetDefinition.storageHash !== savedWidgetDef.storageHash) {
	      // widget definition was found, but storageHash was stale, removing storage
	      $log.info('Widget Definition Object with name "' + savedWidgetDef.name + '" was found ' +
		'but the storageHash property on the widget definition is different from that on the ' +
		'serialized widget loaded from storage. hash from storage: "' + savedWidgetDef.storageHash + '"' +
		', hash from WDO: "' + widgetDefinition.storageHash + '"');
	      continue;
	  }

	  // push instantiated widget to result array
	  result.push(savedWidgetDef);
      }

      return result;
  },

  _handleAsyncLoad: function (promise) {
      // code same as original post...
  }

};

return DashboardState;
}]);

1 个答案:

答案 0 :(得分:2)

只要您进行异步操作,您就应该使公共API异步,即使在某些情况下或大多数情况下它都是同步的。然后,用户的API是一致的。

而不是&#34;检查承诺&#34;,将同步调用转换为承诺。

您的load功能可以简化为以下内容(为了简洁起见,我会留下一些细节,但您应该了解更广泛的概念):

load: function(dashboardId) {

  if (isSync()){
    // sync

    var data = this.storage.getItem(dashboardId);

    // wrap in a promise
    return $q.resolve(_handleSyncLoad(data));

  } else {
    // async

    // return the promise generated by _getItemFromAPI().then()
    return _getItemFromAPI().then(function(data){
      return _handleSyncLoad(data);
    });
  }
}

请注意,我假设_getItemFromAPI() 返回一个承诺(在您的情况下它不会),所以它会如下所示:

function _getItemFromAPI(){

  // ...

  // this "return" is important! it returns the promise generated by $http
  return $http({...}).then(function(response){
    return response.data;
  })
}

这使load的使用保持一致,无论是同步还是异步:

dashboardSvc.load(444).then(function(dashboardData){
  $scope.dashboard = dashboardData;
});