【问题标题】:Angularjs: how can I make a service code "look synchronous"?Angularjs:如何使服务代码“看起来同步”?
【发布时间】:2014-11-18 15:51:18
【问题描述】:

如何使 Angular 服务代码“看起来同步”?

当我清理控制器并将业务逻辑代码放入服务时,我的问题出现了。到目前为止,一切都很好。现在我想在服务函数中“等待”,直到所有异步调用都返回并且 then 返回。我该怎么做?

为了说明我的问题,假设您有一个控制器代码:

  1. 从后端请求一些数据
  2. 对数据进行一些处理并
  3. 将数据交给示波器

这样:

重构前的DataController:

$scope.submitForm = function() {

    RestBackend.query('something').then(function(data) {

        // do some additional things ...
        ...

        $scope.data = data;
    });
};

非常简单。获取数据并填充范围。

重构为控制器+服务后,我最终得到:

DataController 重构:

$scope.submitForm = function() {

    DataService.getData().then(function(data) {
        $scope.data = data;
    });
};

数据服务重构:

this.query = function() {
    var dataDefer = $q.defer();

    RestBackend.query('something').then(function(data) {

        // do some additional things ...
        ...

        dataDefer.resolve(data);
    });
    return dataDefer.promise;
};

我不喜欢在控制器中也必须使用 Promise 的事实。我喜欢承诺,但我想让控制器不知道服务的这个“实施细节”。这就是我希望控制器代码的样子:

DataController(应该如此):

$scope.submitForm = function() {

    $scope.data = DataService.getData();
};

你明白了吗?在控制器中,我不想关心承诺与否。只需等待获取数据然后使用它。因此,我正在寻找一种可能性来实现这样的服务:

  1. 查询数据(异步)
  2. 在获取数据之前不要返回
  3. 返回获取的数据

现在我不清楚第 2 项:我怎样才能“等到数据被提取”然后才继续?目标是服务功能看起来是同步的。

【问题讨论】:

  • 您在项目中使用ngRoute 吗?
  • 我认为你所拥有的是一个非常好的解决方案。您不必等待 promise 被解决,它违背了异步 javascript 的目的。问问自己为什么需要让它运行同步?将我的评论移到答案中
  • @meriadec:是的,我正在使用 ngRoute ...但你为什么要问?

标签: angularjs asynchronous promise angular-promise


【解决方案1】:

我也认为您的解决方案很好。
返回承诺不是服务的实现细节。它是服务 API 的一部分(服务和服务消费者之间的“合同”)。

控制器需要一个承诺,该承诺会使用数据进行解析并按照它认为合适的方式进行处理。
如何构建承诺,如何获取数据等,这些是实现细节。
您可以随时将服务与执行完全不同的服务的服务交换,只要它履行合同(即返回一个在数据准备就绪的情况下解决的承诺)。


也就是说,如果您只使用视图中的数据(即在获取数据后不直接在控制器中对其进行操作),似乎是这种情况,您可以使用ngResources 方法:

返回一个空数组并在获取数据后用数据填充它:

$scope.data = DataService.getData();

// DataService refactored:
this.getData = function () {
    var data = [];

    RestBackend.query('something').then(function(responseData) {
        // do some additional things ...
        ...
        angular.forEach(responseData, function (item) {
            data.push(item);
        });
    });

    return data;
};

顺便说一句,在您当前的(良好)设置中,您需要$q.defer()。您可以只使用承诺链:

this.query = function() {
    return RestBackend.query('something').then(function(data) {
        // do some additional things ...
        ...
        return data;
    });
};

【讨论】:

  • 如果我能接受 2 个答案,我也会接受这个。我将绿色复选标记授予按时间顺序排列的第一个答案。但我不得不说,ExpertSytem 的回答中有非常有用的提示。我求助于投票......
【解决方案2】:

我认为你所拥有的是一个非常好的解决方案。您不必等待 promise 被解决,它违背了异步 javascript 的目的。问问自己为什么需要让它运行同步?

如果你依赖 html 来解决你可以做这样的事情

<div class="alert alert-warning text-center" data-ng-hide="!data.$resolved">
   Got data from service.
</div>

【讨论】:

  • 其实我想在控制器代码中等待数据,而不是在 HTML 模板中。无论如何,谢谢你的提示。另外,在控制器中,我当然想避免 block 并等待,我想知道是否会有某种模式以某种方式进行非阻塞等待...
  • @andmeter:哈哈。非阻塞等待(又名 promise :P)
  • @andmeter 是的,不幸的是没有等待只是承诺或回调方法。
【解决方案3】:

当您使用ngRoute 时,我建议您在路由配置中解析数据,然后视图将被加载一次您的所有数据都将被解析.

$routeProvider
  .when('/your-url', {
    templateUrl: 'path/to/your/template.html',
    controller: 'YourCtrl',

    // that's the point !
    resolve: {
      superAwesomeData: function (DataService) {
        return DataService.getData();
      }
    }
  });

现在,superAwesomeData 可以注入到您的控制器中,它将包含已解析的数据。

angular.module('youModule')
  .controller('YourCtrl', function (superAwesomeData) {

    // superAwesomeData === [...];

  });

【讨论】:

  • 有趣。当我了解 ngRoute 时,我有点忽略了属性“resolve”。所以,我现在又学到了一些东西 ;) 无论如何,AFAIU,你的建议会阻止控制器的实例化,直到一切都得到解决。相反,在我的情况下,我有一个实例化的控制器,并希望在一个处理程序方法中暂停语句流,而不是延迟控制器实例化。
猜你喜欢
  • 2019-10-12
  • 2020-10-05
  • 1970-01-01
  • 2017-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多