$ resource`get`函数如何在AngularJS中同步工作?

时间:2012-08-15 08:35:32

标签: javascript angularjs

我正在观看this AngularJS教程,该教程描述了如何使用Angular资源挂钩Twitter。 (Video tutorial)以下是示例控制器中设置的资源:

$scope.twitter = $resource('http://twitter.com/:action',
    {action: 'search.json', q: 'angularjs', callback: 'JSON_CALLBACK'},
    {get: {method: 'JSONP'}});

该教程显示,有几种方法可以使用get调用从资源中恢复数据。第一种方法是将回调传递给get函数。在ajax请求返回后,将使用结果调用回调:

$scope.twitter.get(function(result) {
    console.log('This was the result:', result);
});

我理解这种方法。这对我来说非常有意义。资源代表Web上可以获取数据的位置,get只是对URL进行ajax调用,返回json,并使用json调用回调函数。 result param是json。

这对我来说很有意义,因为很明显这是一个异步调用。也就是说,在引擎盖下,ajax调用会触发,并且调用后的代码不会被阻止,它会继续执行。然后在稍后的某个不确定点,当xhr成功时,调用回调函数。

然后教程显示了一个看起来更简单的不同方法,但我不明白它是如何工作的:

$scope.twitterResult = $scope.twitter.get();

我假设get下面的xhr必须是异步的,但在这一行中我们将get调用的返回值赋给变量,就像它同步返回一样。

我不理解这个错误吗?怎么可能?我觉得它很有效,但是我觉得它不行。

我理解get可以返回某些内容,而它下面的xhr会异步处理,但如果您自己遵循代码示例,则会看到$scope.twitterResult在执行任何后续行之前获取实际的Twitter内容。例如,如果您在该行之后立即写入console.log($scope.twitterResult),您将在控制台中看到来自twitter的结果,而不是稍后替换的临时值。

更重要的是,因为这是可能的,我如何编写利用相同功能的Angular服务?除了ajax请求之外,还有其他类型的数据存储需要可以在JavaScript中使用的异步调用,我希望能够以这种方式同步编写代码。例如,IndexedDB。如果我能够了解Angular内置资源的用途,我会试一试。

2 个答案:

答案 0 :(得分:63)

$ resource不是同步的,尽管这种语法可能暗示它是:

$scope.twitterResult = $scope.twitter.get();

这里发生的是调用AngularJS后,在调用twitter.get()后,会立即返回,结果为空数组。然后,当异步调用完成并且实际数据从服务器到达时,阵列将使用数据进行更新。 AngularJS将简单地保留对返回的数组的引用,并在数据可用时填充它。

以下是$ magic实现的片段,其中发生了“魔术”:https://github.com/angular/angular.js/blob/master/src/ngResource/resource.js#L372

这也在$resource documentation中描述:

  

重要的是要意识到调用$ resource对象方法会立即返回一个空引用(对象或数组,具体取决于isArray)。从服务器返回数据后,将使用实际数据填充现有引用。这是一个有用的技巧,因为通常将资源分配给模型,然后由视图呈现。具有空对象导致无渲染,一旦数据从服务器到达,则对象用数据填充,并且视图自动重新呈现其自身显示新数据。这意味着在大多数情况下,永远不必为动作方法编写回调函数。

答案 1 :(得分:5)

$ q也可以做到这一点。您可以使用以下内容将普通对象转换为“延迟值”:

var delayedValue = function($scope, deferred, value) {
    setTimeout(function() {
        $scope.$apply(function () {
            deferred.resolve(value);
        });
    }, 1000);
    return deferred.promise;
};

然后在控制器中使用它,以获得与$ scope.twitter.get()在OP示例中所做的类似的效果

angular.module('someApp', [])
.controller('someController', ['$scope', '$q', function($scope, $q) {
  var deferred = $q.defer();
  $scope.numbers = delayedValue($scope, deferred, ['some', 'numbers']);
}]);