我看到的是使用承诺访问FB图形API,Facebook的登录服务的一些例子。
实施例#1:
this.api = function(item) {
var deferred = $q.defer();
if (item) {
facebook.FB.api('/' + item, function (result) {
$rootScope.$apply(function () {
if (angular.isUndefined(result.error)) {
deferred.resolve(result);
} else {
deferred.reject(result.error);
}
});
});
}
return deferred.promise;
}
而且使用的服务"$scope.$digest() // Manual scope evaluation"
时,得到的回应
实施例#2:
angular.module('HomePageModule', []).factory('facebookConnect', function() {
return new function() {
this.askFacebookForAuthentication = function(fail, success) {
FB.login(function(response) {
if (response.authResponse) {
FB.api('/me', success);
} else {
fail('User cancelled login or did not fully authorize.');
}
});
}
}
});
function ConnectCtrl(facebookConnect, $scope, $resource) {
$scope.user = {}
$scope.error = null;
$scope.registerWithFacebook = function() {
facebookConnect.askFacebookForAuthentication(
function(reason) { // fail
$scope.error = reason;
}, function(user) { // success
$scope.user = user
$scope.$digest() // Manual scope evaluation
});
}
}
的jsfiddle
这些问题是:
- 是什么在上面的例子中的区别 ?
- 什么是使用$ Q服务的原因和情况 ?
- 以及它是如何工作的 ?
这不会是一个完整的回答你的问题,但希望这将帮助您和他人当您尝试阅读的文档$q
服务。 我花了一段时间来理解它。
让我们抛开了一会儿AngularJS,只是考虑到Facebook的API调用。 这两个API调用使用回调机制来通知主叫方时,从Facebook的响应可用:
facebook.FB.api('/' + item, function (result) {
if (result.error) {
// handle error
} else {
// handle success
}
});
// program continues while request is pending
...
这是在处理JavaScript和其他语言的异步操作的标准模式。
当你需要执行异步操作,每个操作连续依赖于以前的操作结果的顺序对这种模式的一个大问题就出现了。 这就是这个代码是这样做的:
FB.login(function(response) {
if (response.authResponse) {
FB.api('/me', success);
} else {
fail('User cancelled login or did not fully authorize.');
}
});
首先,它试图登录,然后只验证登录成功它发出请求到图形API之后。
即使在这种情况下,这是只有两个操作串联起来,事情开始变得混乱。 该方法askFacebookForAuthentication
接受失败和成功的回调,但在发生什么FB.login
成功,但FB.api
失败? 此方法总是调用success
回调不管的结果FB.api
方法。
现在,假设你要编写三个或更多的异步操作的一个强大的序列,在每一个步骤,妥善处理错误的方式,将是清晰给任何人,甚至给你几个星期后。 有可能,但它很容易只保留嵌套的回调和失去的轨道前进的道路上的错误。
现在,让我们抛开了一会儿Facebook的API,只是考虑到角承诺API,由执行$q
服务。 该服务实现的模式是企图把异步编程回类似的东西线性一系列简单的语句,用在中的任何一步的能力,“扔”的错误,并在最后处理一下,语义相似熟悉try/catch
块。
考虑这个人为的例子。 假设我们有两个功能,其中第二功能消耗的第一个结果:
var firstFn = function(param) {
// do something with param
return 'firstResult';
};
var secondFn = function(param) {
// do something with param
return 'secondResult';
};
secondFn(firstFn());
现在想象一下,firstFn和secondFn都需要很长的时间才能完成,所以我们要异步处理这个序列。 首先,我们创建一个新的deferred
对象,表示操作的链条:
var deferred = $q.defer();
var promise = deferred.promise;
该promise
属性表示链的最终结果。 如果您在创建后立即登录一个承诺,你会看到,它仅仅是一个空对象( {}
没什么可看的呢,右待着。
到目前为止,我们的承诺,只表示链条的起点。 现在,让我们添加两个操作:
promise = promise.then(firstFn).then(secondFn);
所述then
方法增加了一个步骤到链和然后返回表示伸展链的最终结果的新前景。 只要你喜欢你可以添加尽可能多的步骤。
到目前为止,我们已经建立了我们的功能链,但一切都没有实际发生。 你开始通过调用事物deferred.resolve
,指定要传递给链中的第一个实际步骤的初始值:
deferred.resolve('initial value');
然后......还是没有任何反应。 为了确保模型的变化适当地观察到,角实际上不调用链中的第一个步骤,直到下一次$apply
叫做:
deferred.resolve('initial value');
$rootScope.$apply();
// or
$rootScope.$apply(function() {
deferred.resolve('initial value');
});
因此,有关错误处理的呢? 到目前为止,我们只在链中的每个步骤中指定一个成功的处理程序 。 then
还接受一个错误处理程序作为一个可选的第二个参数。 下面是另一个,一个承诺链,这一次错误处理的详细示例:
var firstFn = function(param) {
// do something with param
if (param == 'bad value') {
return $q.reject('invalid value');
} else {
return 'firstResult';
}
};
var secondFn = function(param) {
// do something with param
if (param == 'bad value') {
return $q.reject('invalid value');
} else {
return 'secondResult';
}
};
var thirdFn = function(param) {
// do something with param
return 'thirdResult';
};
var errorFn = function(message) {
// handle error
};
var deferred = $q.defer();
var promise = deferred.promise.then(firstFn).then(secondFn).then(thirdFn, errorFn);
正如你可以看到在这个例子中,链中的每个处理器都有机会疏导交通到下一个错误处理程序,而不是下一个成功处理程序。 在大多数情况下,你可以在链末端的单个错误处理,但你也可以有一个尝试恢复中间错误处理程序。
要快速返回到您的实例(和你的问题),我只想说,他们代表了两种不同的方式给Facebook的面向回调API适应观测模型的变化角度的方式。 第一个例子包装在一个承诺,其可被添加到一个范围,并且由角的模板系统理解的API调用。 第二个需要直接设置回调结果的范围,然后调用的更强力的办法$scope.$digest()
进行角向意识到来自外部源的变化。
两个例子是不能直接比较,这是因为第一丢失的登录步骤。 然而,这通常需要封装与外部的API像这样单独的服务交互,结果为实现承诺到控制器。 你可以保持你的控制器通过这种方式从外部的关注分开,并与模拟服务更轻松地对其进行测试。
我预期的复杂的答案,将涵盖:为什么它们被普遍使用,以及如何在角使用
这是角承诺MVP的普拉克(最小可行的承诺): http://plnkr.co/edit/QBAB0usWXc96TnxqKhuA?p=preview
资源:
(对于那些懒得点击链接)
的index.html
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.js"></script>
<script src="app.js"></script>
</head>
<body ng-app="myModule" ng-controller="HelloCtrl">
<h1>Messages</h1>
<ul>
<li ng-repeat="message in messages">{{ message }}</li>
</ul>
</body>
</html>
app.js
angular.module('myModule', [])
.factory('HelloWorld', function($q, $timeout) {
var getMessages = function() {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve(['Hello', 'world']);
}, 2000);
return deferred.promise;
};
return {
getMessages: getMessages
};
})
.controller('HelloCtrl', function($scope, HelloWorld) {
$scope.messages = HelloWorld.getMessages();
});
(我知道这并不解决您的具体Facebook的例子,但我发现下面的代码片段很有用)
途经: http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/
更新2014年2月28日: 由于1.2.0,承诺不再通过模板解决。 http://www.benlesh.com/2013/02/angularjs-creating-service-with-http.html
(plunker示例使用1.1.5。)
一个延迟表示asynchronic操作的结果。 它暴露可用于信令的状态和其代表的操作的结果的接口。 它还提供了一种方式来获得相关的承诺实例。
允诺提供了一个接口进行交互与它相关的递延,因此,允许有关方面获得进入状态和递延操作的结果。
当创建一个推迟,它的状态悬而未决,它没有任何结果。 当我们解决()或拒绝()的延迟,它改变它的状态,以解决或拒绝。 不过,我们可以创建一个延迟,甚至分配相互作用与它的未来结果后,立即得到了相关承诺。 只有在推迟或拒绝解决这些互动都会发生。