Я видел несколько примеров служб входа в Facebook, которые использовали обещания для доступа к FB Graph API.
Пример №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
});
}
}
Вопросы следующие:
- В чем разница в приведенных выше примерах?
- Каковы причины и случаи использования услуги $ q ?
- А как это работает ?
Ответы:
Это не будет исчерпывающим ответом на ваш вопрос, но, надеюсь, это поможет вам и другим, когда вы попытаетесь прочитать документацию по
$q
сервису. Мне потребовалось время, чтобы понять это.Давайте на время отложим AngularJS и просто рассмотрим вызовы API Facebook. Оба вызова 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.'); } });
Сначала он пытается войти в систему, а затем, только убедившись, что вход был успешным, делает запрос к Graph API.
Даже в этом случае, который объединяет только две операции, все начинает запутываться. Метод
askFacebookForAuthentication
принимает обратный вызов в случае неудачи и успеха, но что происходит, когда онFB.login
успешен, ноFB.api
терпит неудачу? Этот метод всегда вызываетsuccess
обратный вызов независимо от результатаFB.api
метода.А теперь представьте, что вы пытаетесь закодировать надежную последовательность из трех или более асинхронных операций таким образом, чтобы правильно обрабатывать ошибки на каждом этапе и быть понятными для всех или даже для вас через несколько недель. Возможно, но очень легко просто продолжать встраивать эти обратные вызовы и при этом терять следы ошибок.
Теперь давайте отложим на время API Facebook и просто рассмотрим API Angular Promises, реализованный
$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');
А потом ... по-прежнему ничего не происходит. Чтобы гарантировать правильное наблюдение за изменениями модели, Angular фактически не вызывает первый шаг в цепочке, пока не
$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);
Как вы можете видеть в этом примере, каждый обработчик в цепочке имеет возможность перенаправить трафик на следующий обработчик ошибок, а не на следующий обработчик успеха . В большинстве случаев у вас может быть единственный обработчик ошибок в конце цепочки, но вы также можете иметь промежуточные обработчики ошибок, которые пытаются восстановить.
Чтобы быстро вернуться к вашим примерам (и вашим вопросам), я просто скажу, что они представляют два разных способа адаптации API Facebook, ориентированного на обратный вызов, к способу наблюдения за изменениями модели в Angular. В первом примере вызов API заключен в обещание, которое может быть добавлено в область видимости и понимается системой шаблонов Angular. Второй использует более грубый подход, устанавливая результат обратного вызова непосредственно в области видимости, а затем вызывая,
$scope.$digest()
чтобы Angular узнал об изменении из внешнего источника.Эти два примера нельзя напрямую сравнивать, потому что в первом отсутствует шаг входа в систему. Однако, как правило, желательно инкапсулировать взаимодействия с внешними API, подобными этому, в отдельных сервисах и доставлять результаты контроллерам в виде обещаний. Таким образом, вы можете отделить контроллеры от внешних проблем и упростить их тестирование с помощью имитирующих сервисов.
источник
then
методов является использование$q.all
. Краткое руководство по этому поводу можно найти здесь .$q.all
подходит, если вам нужно дождаться завершения нескольких независимых асинхронных операций. Он не заменяет связывание, если каждая операция зависит от результата предыдущей операции.return 'firstResult'
часть наreturn $q.resolve('firstResult')
, какая будет разница?Это основа для Angular Promises 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/
Обновление от 28 февраля 2014 г. Начиная с версии 1.2.0, обещания больше не разрешаются с помощью шаблонов. http://www.benlesh.com/2013/02/angularjs-creating-service-with-http.html
(в примере плункера используется версия 1.1.5.)
источник
Отложенный представляет собой результат асинхронной операции. Он предоставляет интерфейс, который может использоваться для сигнализации состояния и результата операции, которую он представляет. Он также предоставляет способ получить связанный экземпляр обещания.
Промис предоставляет интерфейс для взаимодействия с относящимся к нему отложенной операцией и, таким образом, позволяет заинтересованным сторонам получить доступ к состоянию и результату отложенной операции.
При создании отложенного состояния оно находится в состоянии ожидания и не имеет результата. Когда мы разрешаем () или reject () отложенный, он меняет свое состояние на разрешено или отклонено. Тем не менее, мы можем получить связанное обещание сразу после создания отложенного и даже назначить взаимодействия с его будущим результатом. Эти взаимодействия будут происходить только после того, как отложенные отклонены или разрешены.
источник
используйте обещание в контроллере и убедитесь, что данные доступны или нет
var app = angular.module("app",[]); app.controller("test",function($scope,$q){ var deferred = $q.defer(); deferred.resolve("Hi"); deferred.promise.then(function(data){ console.log(data); }) }); angular.bootstrap(document,["app"]);
<!DOCTYPE html> <html> <head> <script data-require="angular.js@*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script> </head> <body> <h1>Hello Angular</h1> <div ng-controller="test"> </div> </body> </html>
источник