再次承诺的承诺(Angular JS) [英] The promise of a promise again (Angular JS)

查看:69
本文介绍了再次承诺的承诺(Angular JS)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

已根据请求使用HTTP和初始代码进行了更新/请查看帖子底部:

我最近在AngularJS学习曲线上发布了几个问题,因此SO社区很棒.过去我曾经是传统的C程序员,最近开始编写自己的ionic/Angular JS应用程序.在将自定义函数转换为Promise时,我在传统异步调用的Promise版本方面苦苦挣扎.我认为我不太了解,我发现各种示例都非常人为.我将不胜感激.我有一些无法正常工作的代码,还有一些概念性问题:

让我们使用这个简单的功能:

angular.module('zmApp.controllers').service('ZMDataModel', function() { return { getMonitors: function () { return monitors; } }

getMonitors是一个简单的函数,基本上返回一个监视器数组.但是有一个难题:当应用程序首次启动时,我调用了一个执行http获取操作的http工厂,然后开始填充此监视器列表.此http工厂与此服务不同,但会在此服务中调用setMonitor方法以填充数组.填充数组后,将名为"monitorsLoaded"的变量设置为1.当将此变量设置为1时,我确定监视器已加载.

现在,我有一个名为"MontageCtrl"的控制器的视图.我想等待监视器加载后再显示视图.在上一篇文章中,一个人建议我使用路由解析,但是我必须先将getMonitors转换为Promise.所以这就是我所做的:

angular.module('zmApp.controllers').service('ZMDataModel',  function($q) {
getMonitors: function () {

            var _deferred  = $q.defer();
            if (monitorsLoaded!=0)
            {
                console.log ("**** RETURNING MONITORS *****");
                    _deferred.resolve(monitors);
            }
            console.log ("*** RETURNING PROMISE ***");
            return _deferred.promise;
        },

接下来,在app.js中,我按如下所示连接了路由:

  .state('app.montage', {
               data: {requireLogin:false},
            resolve: {
                        message: function(ZMDataModel) 
                        {
                            console.log ("Inside app.montage resolve");
                          return ZMDataModel.getMonitors();
                        }
            }, 

最后,我修改了控制器,以实现如下承诺:

 angular.module('zmApp.controllers').controller('zmApp.MontageCtrl', function($scope,$rootScope, ZMHttpFactory, ZMDataModel,message) {
        
            //var monsize =3;
            console.log ("********* Inside Montage Ctrl"); 

似乎基于日志,我从不进入Montage Ctrl.路由解析似乎一直在等待,而我的日志显示一段时间后,monitorLoaded设置为1.

我有几个概念性问题:

a)在示例中精心设计的getMonitors函数中,为什么人们返回_deferred.promise但仅分配_deferred.resolve? (即为什么不退还它?).它会自动返回吗?

b)我注意到,如果我将var _deferred定义移到我的服务中并移出其子函数,它确实起作用了,但是具有相同路由依赖关系的下一个视图却没有起作用.我很困惑.

c)最后,我准备在某个地方进行路由解析时,服务和工厂之间是有区别的,因为服务仅实例化一次.我也很困惑,因为在某些途径中,人们在使用示例时会使用示例,而我使用的是.state. 在这个阶段,我深陷自己的困惑中.有人可以帮忙澄清一下吗?我真正想要的是让各种视图都等到monitorsLoaded为1为止.而且我想通过路由解析和Promise来做到这一点,所以我一劳永逸地摆脱了Promise的困扰.

已添加:以下是HTTP工厂代码以及在应用首次启动时调用此代码的app.run代码.仅供参考,http工厂运行良好-当我编写ZMDataModel时问题就开始出现-我希望它成为供所有控制器使用的中央数据存储库-因此,他们不必每次都调用HTTP Factory来访问数据,因此我可以控制何时需要调用HTTP工厂

 angular.module('zmApp.controllers').factory('ZMHttpFactory', ['$http', '$rootScope','$ionicLoading', '$ionicPopup','$timeout','ZMDataModel',
  function($http, $rootScope, $ionicLoading, $ionicPopup, $timeout,ZMDataModel) {



    return {
      getMonitors: function() {
                                             
        var monitors = [];
        var apiurl = ZMDataModel.getLogin().apiurl;
        var myurl = apiurl+"/monitors.json";
        
        return $http({
          url: myurl,
          method: 'get'
                  
        }) //http
        .then(function(response) {
              var data = response.data;
              //console.log("****YAY" + JSON.stringify(data));
          // $rootScope.$broadcast ('handleZoneMinderMonitorsUpdate',monitors);
            $ionicLoading.hide();
            
            ZMDataModel.setMonitors(data.monitors);
            ZMDataModel.setMonitorsLoaded(1);
            //monitors = data.monitors;
            return ZMDataModel.getMonitors();
            },
            function (result)
            {
                console.log ("**** Error in HTTP");
                $ionicLoading.hide();
                ZMDataModel.setMonitorsLoaded(1);
                //$ionicPopup.alert ({title: "Error", template:"Error retrieving Monitors. \nPlease check if your Settings are correct. "});
                return  ZMDataModel.getMonitors();
            }
        ); //then
      }, //getMonitors 

这是app.run中的代码,该代码首先调用此代码:

 .run(function($ionicPlatform, $ionicPopup, $rootScope, $state,ZMDataModel, ZMHttpFactory)
{
    
    ZMDataModel.init();
    var loginData = ZMDataModel.getLogin();
    
        if ( loginData.username && loginData.password && loginData.url && loginData.apiurl)
            {
                console.log ("VALID CREDENTIALS. Grabbing Monitors");
                // this calls http factory getMonitors that eventually populated the ZMDataModel 
                // monitors array and sets monitorsLoaded to 1
                ZMHttpFactory.getMonitors();
               
            }
} 

解决方案

我终于解决了所有问题.我的最初尝试存在各种问题.我最终的解决方案是在这里我是否正确地兑现了这一承诺?

经验教训:

a)将HTTP进入工厂,然后将数据模型分为另一个服务,这不必要地使生活变得复杂.但是,分离并不是问题.实际上,在上面第一次运行诺言的编码方式是,如果monitorsLoaded为0,它将简单地返回延迟的诺言,并且没有".success"或类似的构造让我再次进入解析代码块.

b)让我循环跑动的最大事情是推迟或拒绝仅仅是设置状态.回报永远是承诺-它会返回您设置的状态.我以为return d.promise总是意味着返回进行中".

Updated with HTTP and initial code based on requests/Please look at the bottom of the post:

I've been posting several questions on my AngularJS learning curve of late and the SO community has been fantastic. I've been a traditional C programmer when I used to program and have recently started writing my own ionic/Angular JS app. I'm struggling with the promise version of traditional async calls when it comes to converting a custom function to a promise. I don't think I really understood and I find various examples very contrived. I'd appreciate some help. I have some code which is not working, and I have some conceptual questions:

Let's take this simple function:

angular.module('zmApp.controllers').service('ZMDataModel', function() { return { getMonitors: function () { return monitors; } }

getMonitors is a simple function that basically returns an array of monitors. But here is the rub: When the app first starts, I call an http factory that does an http get and goes about populating this monitor list. This http factory is different from this service but invokes a setMonitor method in this service to populate the array. When the array is populated, a variable called 'monitorsLoaded' is set to 1. When this variable is set to 1, I know for sure monitors is loaded.

Now, I have a view with a controller called "MontageCtrl". I want to wait for the monitors to load before I show the view. In a previous post, one person suggested I use route resolve, but I had to first convert my getMonitors to a promise. So here is what I did:

angular.module('zmApp.controllers').service('ZMDataModel',  function($q) {
getMonitors: function () {

            var _deferred  = $q.defer();
            if (monitorsLoaded!=0)
            {
                console.log ("**** RETURNING MONITORS *****");
                    _deferred.resolve(monitors);
            }
            console.log ("*** RETURNING PROMISE ***");
            return _deferred.promise;
        },

Next up, in app.js I connected the route as follows:

 .state('app.montage', {
               data: {requireLogin:false},
            resolve: {
                        message: function(ZMDataModel) 
                        {
                            console.log ("Inside app.montage resolve");
                          return ZMDataModel.getMonitors();
                        }
            },

Finally I modified my controller to grab the promise as such:

angular.module('zmApp.controllers').controller('zmApp.MontageCtrl', function($scope,$rootScope, ZMHttpFactory, ZMDataModel,message) {
        
            //var monsize =3;
            console.log ("********* Inside Montage Ctrl");

It seems based on logs, I never go inside Montage Ctrl. Route resolve seems to be waiting for ever, whereas my logs are showing that after a while, monitorLoaded is being set to 1.

I have several conceptual questions:

a) In function getMonitors, which I crafted as per examples, why do people return a _deferred.promise but only assign a _deferred.resolve? (i.e. why not return it too?). Does it automatically return?

b) I noticed that if I moved var _deferred definition to my service and out of its sub function, it did work, but the next view that had the same route dependency did not. I'm very confused.

c) Finally I ready somewhere that there is a distinction between a service and a factory when it comes to route resolve as a service is only instantiated once. I am also very confused as in some route resolve examples people use when, and I am using .state. At this stage, I'm deep into my own confusion. Can someone help clarify? All I really want is for various views to wait till monitorsLoaded is 1. And I want to do it via route resolves and promises, so I get the hang of promises once and for all.

Added: Here is the HTTP factory code as well as the app.run code that calls this when the app first starts. FYI, the http factory works well - the problems started when I crafted ZMDataModel - I wanted this to be a central data repository for all controllers to use -- so they did not have to call HTTP Factory each time to access data, and I could control when HTTP factory needs to be called

angular.module('zmApp.controllers').factory('ZMHttpFactory', ['$http', '$rootScope','$ionicLoading', '$ionicPopup','$timeout','ZMDataModel',
  function($http, $rootScope, $ionicLoading, $ionicPopup, $timeout,ZMDataModel) {



    return {
      getMonitors: function() {
                                             
        var monitors = [];
        var apiurl = ZMDataModel.getLogin().apiurl;
        var myurl = apiurl+"/monitors.json";
        
        return $http({
          url: myurl,
          method: 'get'
                  
        }) //http
        .then(function(response) {
              var data = response.data;
              //console.log("****YAY" + JSON.stringify(data));
          // $rootScope.$broadcast ('handleZoneMinderMonitorsUpdate',monitors);
            $ionicLoading.hide();
            
            ZMDataModel.setMonitors(data.monitors);
            ZMDataModel.setMonitorsLoaded(1);
            //monitors = data.monitors;
            return ZMDataModel.getMonitors();
            },
            function (result)
            {
                console.log ("**** Error in HTTP");
                $ionicLoading.hide();
                ZMDataModel.setMonitorsLoaded(1);
                //$ionicPopup.alert ({title: "Error", template:"Error retrieving Monitors. \nPlease check if your Settings are correct. "});
                return  ZMDataModel.getMonitors();
            }
        ); //then
      }, //getMonitors

And here is the code in app.run that first calls this:

.run(function($ionicPlatform, $ionicPopup, $rootScope, $state,ZMDataModel, ZMHttpFactory)
{
    
    ZMDataModel.init();
    var loginData = ZMDataModel.getLogin();
    
        if ( loginData.username && loginData.password && loginData.url && loginData.apiurl)
            {
                console.log ("VALID CREDENTIALS. Grabbing Monitors");
                // this calls http factory getMonitors that eventually populated the ZMDataModel 
                // monitors array and sets monitorsLoaded to 1
                ZMHttpFactory.getMonitors();
               
            }
}

解决方案

I finally solved all the problems. There were various issues with my initial attempts. My final resolved solution is here Am I returning this promise correctly?

The learnings:

a) Separating the HTTP get into a factory and the data model into another service was unnecessarily complicating life. But that separation was not the problem. Infact, the way the promise was coded above, on first run, if monitorsLoaded was 0, it would simply return the deferred promise and there was no ".success" or similar construct for me to get into the resolve code block again.

b) The biggest thing that was making me run around in loops was deferring or rejecting was simply setting a state. the return always has to be the promise - and it would return the state you set. I assumed return d.promise always means returning "in progress".

这篇关于再次承诺的承诺(Angular JS)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆