现在我知道,由于javascript的执行方式,建议您将所有远程请求作为异步而不是同步运行。虽然我同意99%的时间,但有时您确实希望将远程请求作为同步而不是异步运行。例如,加载会话数据是我想要同步进行的操作,因为我不希望在加载数据之前呈现任何视图。这个plunker显示异步加载会话数据的问题(注意:我使用$ timeout来模拟异步调用会发生什么):
http://plnkr.co/edit/bzE1XP23MkE5YKWxRYrn?p=preview
data属性不会加载任何内容,因为数据在尝试获取数据时不可用而data2只是因为数据在尝试获取数据时可用。现在,在这种情况下,我可以将会话变量放在范围内并使用它完成,但情况并非总是如此。
除了使用jQuery的.ajax()方法(尝试尽可能少地依赖jQuery)之外,是否有更好的方法在角度应用程序中同步远程调用?
答案 0 :(得分:7)
如果您希望在加载控制器之前加载会话数据,则应将其作为resolve
参数包含(假设您使用的是$routeProvider
)。
例如:
angular.module('mymodule', ['ngResource'])
/* here's our session resource. we can call Session.get() to retrieve it. */
.factory('Session', ['$resource', function($resource) {
return $resource('/api/session.json');
}])
/* here's our controller + route definition. */
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/foo', {
controller: 'MyCtrl',
templateUrl: '/templates/foo.html',
/* the controller will not be loaded until the items
* below are all resolved! */
resolve: {
session: ['$q', 'Session', function($q, Session) {
var d = $q.defer();
Session.get(function(session) {
/* session returned successfully */
d.resolve(session);
}, function(err) {
/* session failed to load */
d.reject(err);
});
return d.promise;
}]
}
});
}])
.controller('MyCtrl', ['$scope', 'session', function($scope, session) {
/* 'session' here is the key we passed to resolve above.
* It will already be loaded and resolved before this function is called */
$scope.session = session;
}]);
答案 1 :(得分:3)
Angular被硬编码以使请求异步。要同步执行它将采用其他代码,无论是自定义还是来自其他库。这是从角度1.0.7开始的第9269行:
xhr.open(method, url, true);
第三个参数使它异步。
我会后退一步,思考你是如何做事的。您可以在异步请求进行时提供一些加载指示器,并轻松控制成功回调中视图的加载,以便在加载数据之前不会显示。
答案 2 :(得分:-1)
更好的解决方案是添加响应拦截器:
checkAuth = ($q, $location) ->
success = (response) ->
response
error = (response) ->
errorCode = response.status
$location.path '/login' if errorCode is 403 or errorCode is 401
# $q.reject response - no need because we are redirecting before any other promises in the chain will resolve (were breaking our future promises)
(promise) ->
promise.then success, error
$httpProvider.responseInterceptors.push checkAuth
在我的$ routeProvider中,或者在我的情况下为$ stateProvider:
.state 'user',
templateUrl: 'assets/views/user/partials/user.html'
resolve:
credentials: (checkLogIn) ->
checkLogIn.get().$promise
当checkLogIn.get()的promise被拒绝时(错误处理程序被触发),假设它是401或403响应(未经身份验证或未经授权),则承诺链将被破坏,用户将被“重定向”到/登录。
使用此方法,任何错误调用都将被引导到拦截器,而不是逐个路径地处理错误。