当前位置: 首页 > 知识库问答 >
问题:

AngularJS:使用异步数据初始化服务

崔宜修
2023-03-14

我有一个AngularJS服务,我想用一些异步数据初始化它。类似这样的事情:

myModule.service('MyService', function($http) {
    var myData = null;

    $http.get('data.json').success(function (data) {
        myData = data;
    });

    return {
        setData: function (data) {
            myData = data;
        },
        doStuff: function () {
            return myData.getSomeData();
        }
    };
});

显然,这是行不通的,因为如果在mydata返回之前,有东西试图调用dostuff(),我将得到一个空指针异常。据我所知,从这里和这里所问的其他一些问题中,我有几个选择,但没有一个看起来很清楚(也许我遗漏了什么):

使用“运行”设置服务

设置应用程序时,请执行以下操作:

myApp.run(function ($http, MyService) {
    $http.get('data.json').success(function (data) {
        MyService.setData(data);
    });
});

那么我的服务将如下所示:

myModule.service('MyService', function() {
    var myData = null;
    return {
        setData: function (data) {
            myData = data;
        },
        doStuff: function () {
            return myData.getSomeData();
        }
    };
});

这在某些时候起作用,但如果异步数据的时间恰好比初始化所有内容所需的时间长,则在调用dostuff()时会出现空指针异常

使用promise对象

这可能有用。唯一的缺点是,在我调用MyService的任何地方,我都必须知道doStuff()返回一个Promission,并且所有代码都必须提供给我们then以便与Promission交互。我宁愿等到myData返回后再加载my应用程序。

手动引导

angular.element(document).ready(function() {
    $.getJSON("data.json", function (data) {
       // can't initialize the data here because the service doesn't exist yet
       angular.bootstrap(document);
       // too late to initialize here because something may have already
       // tried to call doStuff() and would have got a null pointer exception
    });
});

全局Javascript Var我可以将JSON直接发送到全局Javascript变量:

HTML:

<script type="text/javascript" src="data.js"></script>

data.js:

var dataForMyService = { 
// myData here
};

则在初始化myservice时可用:

myModule.service('MyService', function() {
    var myData = dataForMyService;
    return {
        doStuff: function () {
            return myData.getSomeData();
        }
    };
});

这也可以工作,但我有一个全局javascript变量,气味很难闻。

这是我唯一的选择吗?这些选项中有一个比其他选项好吗?我知道这是一个相当长的问题,但我想表明我已经尝试探索我的所有选择。如有任何指导,将不胜感激。

共有2个答案

高胜
2023-03-14

基于Martin Atkins的解决方案,这里有一个完整、简洁的纯角度解决方案:

(function() {
  var initInjector = angular.injector(['ng']);
  var $http = initInjector.get('$http');
  $http.get('/config.json').then(
    function (response) {
      angular.module('config', []).constant('CONFIG', response.data);

      angular.element(document).ready(function() {
          angular.bootstrap(document, ['myApp']);
        });
    }
  );
})();

该解决方案使用一个自动执行的匿名函数来获取$HTTP服务,请求配置,并在配置变得可用时将其注入到名为config的常量中。

完成后,我们等待文档准备就绪,然后引导Angular应用程序。

这比Martin的解决方案稍有增强,Martin的解决方案将获取配置推迟到文档准备就绪之后。据我所知,没有理由因此而延迟$HTTP调用。

单元测试

注意:我发现当代码包含在app.js文件中时进行单元测试时,这个解决方案不能很好地工作。出现这种情况的原因是,当加载JS文件时,上面的代码立即运行。这意味着测试框架(在我的例子中是Jasmine)没有机会提供$HTTP的模拟实现。

我的解决方案是将这段代码移动到index.html文件中,这样Grunt/karma/jasmine单元测试基础结构就看不到它了,我对此并不完全满意。

充煌
2023-03-14

您看过$RouteProvider.When(“/path”,{resolve:{...}吗?它可以使承诺方法更清晰:

在您的服务中暴露一个承诺:

app.service('MyService', function($http) {
    var myData = null;

    var promise = $http.get('data.json').success(function (data) {
      myData = data;
    });

    return {
      promise:promise,
      setData: function (data) {
          myData = data;
      },
      doStuff: function () {
          return myData;//.getSomeData();
      }
    };
});

解析添加到路由配置:

app.config(function($routeProvider){
  $routeProvider
    .when('/',{controller:'MainCtrl',
    template:'<div>From MyService:<pre>{{data | json}}</pre></div>',
    resolve:{
      'MyServiceData':function(MyService){
        // MyServiceData will also be injectable in your controller, if you don't want this you could create a new promise with the $q service
        return MyService.promise;
      }
    }})
  }):

在解决所有依赖项之前,您的控制器不会被实例化:

app.controller('MainCtrl', function($scope,MyService) {
  console.log('Promise is now resolved: '+MyService.doStuff().data)
  $scope.data = MyService.doStuff();
});

我在plnkr:http://plnkr.co/edit/gkg21xh0rwcmeqgudzkh?p=preview上举了一个例子

 类似资料:
  • 我有一个AngularJS服务,我想用一些异步数据初始化它。类似这样的事情: 显然,这是行不通的,因为如果在返回之前,有东西试图调用,我将得到一个空指针异常。据我所知,从这里和这里所问的其他一些问题中,我有几个选择,但没有一个看起来很清楚(也许我遗漏了什么): 使用“运行”设置服务 设置应用程序时,请执行以下操作: 那么我的服务将如下所示: 这在某些时候起作用,但如果异步数据的时间恰好比初始化所有

  • 问题内容: 我在尝试使用异步数据初始化过滤器时遇到麻烦。 过滤器非常简单,它需要将路径转换为名称,但是这样做需要一个对应数组,我需要从服务器中获取该数组。 在返回函数之前,我可以在过滤器定义中执行操作,但是异步方面阻止了该操作 使用诺言可能是可行的,但我对角负载如何过滤没有明确的了解。这篇文章解释了如何通过服务实现这种魔力,但是是否有可能对过滤器做同样的事情? 而且,如果有人对如何翻译这些路径有更

  • SOFABoot 提供了模块并行启动以及 Spring Bean 异步初始化能力,用于加快应用启动速度。本文介绍如何使用 SOFABoot 异步初始化 Spring Bean 能力以提高应用启动速度。 使用场景 在实际使用 Spring/Spring Boot 开发中,一些 Bean 在初始化过程中执行准备操作,如拉取远程配置、初始化数据源等等。在应用启动期间,这些 Bean 会增加 Spring

  • 我在尝试用异步数据初始化过滤器时遇到了麻烦。 过滤器非常简单,它需要将路径转换为名称,但要做到这一点,它需要一个对应数组,我需要从服务器获取该数组。 在返回函数之前,我可以在过滤器定义中做一些事情,但是异步方面阻止了这一点 使用promise可能是可行的,但我不清楚角度负载是如何过滤的。这篇文章解释了如何通过服务实现这样的魔力,但是对于过滤器也可以这样做吗? 如果有人对如何翻译这些路径有更好的想法

  • 初始化数据 打开MainSetup类,在Daos语句后面插入新建根用户的代码 // 初始化默认根用户 if (dao.count(User.class) == 0) { User user = new User(); user.setName("admin"); user.setPassword

  • 问题内容: 我想以异步方式初始化模块,并提出一些想法。我需要具有Mongo集合列表和其他数据的DB对象,但是为了简洁起见,其中的文件列表确实有用。 我无法导出函数或类,因为我每次都需要返回相同的对象。 首先 想到的最简单的方法是分配给它,然后填充它: 不好的事情–我真的从外面不知道什么时候准备好列表,也没有检查错误的好方法。 *我想出的 *第二种 方法是继承并通知所有人数据库已准备就绪或发生了错误