【问题标题】:How does the $resource `get` function work synchronously in AngularJS?$resource `get` 函数如何在 AngularJS 中同步工作?
【发布时间】:2012-08-11 13:38:13
【问题描述】:

我正在观看thisAngularJS 教程,该教程描述了如何使用 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);
});

我理解这种方法。这对我来说很有意义。该资源代表网络上一个可以获取数据的地方,get 只是对 url 进行 ajax 调用,获取 json,然后使用 json 调用回调函数。 result 参数就是那个 json。

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

然后教程展示了另一种方法,看起来简单很多,但是我不明白它是如何工作的:

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

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

我不理解这一点是错的吗?这怎么可能?我觉得它真的很好用,我就是不明白。

我知道get 可以返回 something 而它下面的 xhr 关闭并异步处理,但是如果您自己按照代码示例进行操作,您会看到 $scope.twitterResult 获取实际的 twitter执行任何后续行之前的内容。例如,如果您在该行之后立即写 console.log($scope.twitterResult),您将看到来自 twitter 的结果记录在控制台中,而不是稍后替换的临时值。

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

【问题讨论】:

  • 仅供参考,不是 AJAX 请求,否则必须使用 CORS。 JSONP 是 !== XMLHttpRequest();
  • 您在这里基本上是在谈论承诺。 $http 和 $resource 现在都支持 Promise。

标签: javascript angularjs


【解决方案1】:

$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']);
}]);

【讨论】:

    【解决方案2】:

    $resource 不是同步的,尽管此语法可能表明它是:

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

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

    这是发生“魔术”的 $resource 实现的片段:https://github.com/angular/angular.js/blob/master/src/ngResource/resource.js#L372

    $resource documentation 中也对此进行了描述:

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

    【讨论】:

    • 是的,这对我来说很有意义。对我来说没有意义的是这里的console.log 行不会打印一个空引用,它会打印一个来自推特的真实结果:$scope.twitterResult = $scope.twitter.get();console.log('result', $scope.twitterResult); 怎么可能在下一个之前填充数据代码行?另外,感谢您的参考。
    • 在console.log() 上进行中继,因为我看到console.log 和打印更新的数组有一些奇怪的效果(基本上有时日志会获取更改并显示它们!)。要查看发生了什么,请尝试以下操作:console.log(angular.copy($scope.twitterResult)),因此请确保您正在打印调用中的值。这实际上是关于时间的,您可能会在控制台有机会打印任何内容之前从 $resource 获得结果......
    • 你知道 - 你是对的。 console.log 是罪魁祸首。如果我这样做,我会得到一个空对象:alert(JSON.stringify($scope.twitterResult)); 感谢您的帮助!
    • 我只是想在这里弹出并指出,使用 AngularJS 1.2,模板中的承诺的自动解包不再发生。如果您只是将您从 $http、$resource 等中获得的承诺直接分配给 $scope 变量,那么当您在视图中引用它们时,不要期望它们会起作用。他们不会。有关详细信息,请参阅他们的迁移文档:docs.angularjs.org/guide/migration
    猜你喜欢
    • 2013-08-29
    • 2015-10-01
    • 2015-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多