使用 $q promise 就像从 $resource 返回的 promise

use the $q promise like the promise returned from $resource

使用 angular 的 $resource promise,我可以将 promise 重新分配给变量并使用它,而无需在成功函数中将 promise 的结果分配给变量。我想知道我是否可以用 $q?

达到同样的效果

示例

.service("rest", function($resource){
        return {
            user: $resource( "http://some.com/url")
        }
    })
    .controller("myCtrl", function($scope, rest){
        $scope.user = rest.user.get(); <-- This is what I want to be able to do with $q
    })

简短回答:否

$resource 界面是专门为允许这种 'futures' 行为而设计的。当您调用 .get() 时,它 returns 一个占位符对象,当它可用时将填充数据。

因为 $resource 是一个内部 Angular 工厂,它知道何时触发摘要循环,这让您看起来可以从一开始就使用 $scope.user。实际上,在服务器收到 HTTP 响应之前,它不会填充数据。

另一方面,承诺是一种通用机制。当值到达时,它不知道将其值存储在哪种占位符对象中。它也不会到达什么样的价值。所以 promise 公开了一系列回调方法,我们可以使用这些方法让我们知道 promise 何时准备就绪。


长答案:某种程度上(您可以使用承诺来构建像这样工作的接口)

这种机制之所以有效,是因为 Angular 对作用域进行脏检查的方式。当异步事件发生时,需要让 Angular 知道它应该检查是否有任何更改并且需要更新它的视图。

当数据从我们的 HTTP 响应到达时,$resource 工厂负责通知 Angular。这意味着 $scope.user 对象将在到达后立即显示在您的视图中。 $resource 负责在异步事件发生时让事情发生。在这种情况下,异步事件是随数据到达的 HTTP 响应。

使用 promises 时,您负责在异步事件发生时让事情发生。 promise 将特定于 Angular,因此 promises 有一个通用接口来处理可能发生在 promise 上的不同异步事件。当其中之一发生时,您的工作就是完成工作,这就是为什么简短的回答是否定的。

但是,如果您正在实施 factory/service/provider,这种行为是可取的,这是可能的。

让我们创建一个火箭工厂来创建火箭实例,稍后将添加它们的零件,就像 $resource 使用 $q 工厂一样。

app.factory('Rocket', function(Junkyard) {
  return function(name) {
    var rocket = {};

    rocket.name = name || 'Boring Rocket';
    rocket.parts = [];
    rocket.promise = Junkyard.getParts();

    // Junkyard.getParts returns a $q promise
    // which will be resolved with an object
    // of things that can be attached to the rocket.
    //
    // It might look something like this
    // [
    //   { name: "thrusters", "quantity":  3 },
    //   { name: "engines", "quantity": 100 },
    //   { name: "robots", "quantity": 2 }
    // ]

    rocket.promise.then(function(parts) {
      parts.forEach(function(part) {
        rocket.parts.push(part);
      });
    });

    return rocket;
  };
});

现在我们可以像 $resource 一样使用我们的新工厂:

function MyController($scope, Rocket) {
  $scope.rocket = Rocket('Top Rocket');
  // we can use $scope.rocket here!

  $scope.rocket.name = 'Even Better Rocket';
  // but it won't have the properties from
  // Junkyard.getParts yet.
}

我们可以将这些值直接模板化到我们的视图中。

<section ng-controller='MyController'>
  <h1 ng-bind='rocket.name'></h1>
  <table>
    <th>
      <td>Name</td>
    </th>
    <th>Description</th>
    <tr ng-repeat='part in rocket.parts'>
      <td ng-bind='part.name'></td>
      <td ng-bind='part.description'></td>
    </tr>
  </table>
</section>

一旦 promise 解决,属性将被添加到火箭并且范围摘要将被 $q 触发,因为 $q 知道异步事件刚刚发生。

将承诺直接分配给变量并在模板中呈现它们的功能已从 Angular 中删除。请参阅此重大更改:Templates no longer automatically unwrap promises。相反,当承诺已解决或被拒绝时,您必须在回调中分配范围 属性。

您可以 return 更新一个对象而不是承诺。 jsfiddle

var getUser = function(base) {
    var user = base || {};

    // replace with actual request
    $timeout(function() {    
        // make sure to use angular.copy so the same object is used
        angular.copy({
            id: 1,
            username: 'anon',
            roles: [
                { id: 1, title: 'user' },
                { id: 2, title: 'admin' },
                { id: 3, title: 'editor' }
            ]
        }, user);
    }, 3000);
    return user;
};

$scope.user = getUser({roles: []});

这样做有效(它是一个 coffeescript),

service.getSomeData = () ->
    data = {}
    $http.get("/getSomeData").then (resp) ->
      angular.copy(resp.data, data)

   return data

在控制器中你可以这样做

 $scope.data = service.getSomeData()