$ resource`get`函数如何在AngularJS中同步工作?
我正在观看这个 AngularJS教程,介绍如何使用Angular资源连接到Twitter。 ( video教程 )以下是在示例控制器中设置的资源:
$scope.twitter = $resource('http://twitter.com/:action', {action: 'search.json', q: 'angularjs', callback: 'JSON_CALLBACK'}, {get: {method: 'JSONP'}});
本教程显示了使用get
调用从资源获取数据的方法。 第一种方法是将callback传递给get函数。 callback将在ajax请求返回后调用结果:
$scope.twitter.get(function(result) { console.log('This was the result:', result); });
我明白这个方法。 这对我来说非常有意义。 这个资源代表了一个可以获取数据的地方,只需要调用一个url的ajax,获取json,然后用json调用callback函数。 result
参数是json。
这对我来说是有道理的,因为这显然是一个asynchronous调用。 也就是说,在引擎盖下,ajax调用触发,并且调用之后的代码不被阻塞,它继续执行。 然后在稍后的某个不确定点,当xhr成功时,调用callback函数。
然后教程显示了一个看起来更简单的不同方法,但我不明白它是如何工作的:
$scope.twitterResult = $scope.twitter.get();
我假设下面的xhr必须是asynchronous的,但是在这一行中,我们将get
调用的返回值分配给一个variables,就像它同步返回一样。
我不明白这个错误吗? 这怎么可能? 我认为它的工作真的很好,我只是不明白。
我明白, get
可以返回一些东西,而它下面的xhr会asynchronous处理,但是如果你自己跟着代码示例,你会发现$scope.twitterResult
在执行任何后续行之前都会得到实际的twitter内容。 例如,如果您在该行之后立即编写console.log($scope.twitterResult)
,您将看到在控制台中logging的twitter结果,而不是稍后replace的临时值。
更重要的是,因为这是可能的,我怎样才能编写一个利用这个相同function的Angular服务呢? 除了Ajax请求,还有其他types的数据存储需要asynchronous调用,可以在JavaScript中使用,我希望能够以这种风格同步编写代码。 例如,IndexedDB。 如果我可以把我的头围绕Angular的内置资源如何做,我会给它一个镜头。
$资源是不同步的,虽然这个语法可能表明它是:
$scope.twitterResult = $scope.twitter.get();
这里发生的事情是,调用AngularJS将在调用twitter.get()
之后立即返回,结果是一个空数组。 然后,当asynchronous调用完成并且真实数据从服务器到达时,数组将被更新为数据 。 AngularJS将简单地保留对返回数组的引用,并在数据可用时填充它。
这里是“魔术”发生的$资源实施的片段: https : //github.com/angular/angular.js/blob/master/src/ngResource/resource.js#L372
这也在$ resource文档中描述:
认识到调用一个$ resource对象方法立即返回一个空引用(依赖于
isArray
对象或数组)是很重要的。 一旦数据从服务器返回,现有的引用将填充实际的数据。 这是一个很有用的技巧,因为通常资源被分配到一个模型,然后由视图渲染。 有一个空对象导致不呈现,一旦数据从服务器到达,然后对象填充数据和视图自动重新呈现自己显示新的数据。 这意味着在大多数情况下,不必为动作方法编写callback函数。
$ 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']); }]);