Vi alguns exemplos de serviços de login do Facebook que estavam usando promessas para acessar a API do FB Graph.
Exemplo 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;
}
E serviços usados "$scope.$digest() // Manual scope evaluation"
quando obtivemos a resposta
Exemplo 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
});
}
}
As perguntas são:
- Qual é a diferença nos exemplos acima?
- Quais são os motivos e casos para usar o serviço $ q ?
- E como isso funciona ?
Respostas:
Essa não será uma resposta completa para sua pergunta, mas espero que isso ajude você e outras pessoas quando tentar ler a documentação no
$q
serviço. Demorei um pouco para entender.Vamos anular o AngularJS por um momento e considerar as chamadas da API do Facebook. As duas chamadas de API usam um mecanismo de retorno de chamada para notificar o chamador quando a resposta do Facebook estiver disponível:
Este é um padrão padrão para lidar com operações assíncronas em JavaScript e outros idiomas.
Um grande problema com esse padrão surge quando você precisa executar uma sequência de operações assíncronas, em que cada operação sucessiva depende do resultado da operação anterior. É isso que este código está fazendo:
Primeiro, ele tenta efetuar login e, somente depois de verificar se o logon foi bem-sucedido, faz a solicitação à API do Graph.
Mesmo neste caso, que está encadeando apenas duas operações, as coisas começam a ficar confusas. O método
askFacebookForAuthentication
aceita um retorno de chamada por falha e sucesso, mas o que acontece quandoFB.login
é bem-sucedido, masFB.api
falha? Este método sempre chama osuccess
retorno de chamada, independentemente do resultado doFB.api
método.Agora imagine que você está tentando codificar uma sequência robusta de três ou mais operações assíncronas, de maneira que lide adequadamente com os erros a cada etapa e seja legível para qualquer outra pessoa ou mesmo para você depois de algumas semanas. Possível, mas é muito fácil continuar aninhando esses retornos de chamada e perder o controle de erros ao longo do caminho.
Agora, vamos anular a API do Facebook por um momento e considerar a API de Promessas Angulares, conforme implementada pelo
$q
serviço. O padrão implementado por este serviço é uma tentativa de transformar a programação assíncrona novamente em algo semelhante a uma série linear de instruções simples, com a capacidade de 'lançar' um erro em qualquer etapa do processo e manipulá-lo no final, semanticamente semelhante aotry/catch
bloco familiar .Considere este exemplo artificial. Digamos que temos duas funções, em que a segunda função consome o resultado da primeira:
Agora imagine que firstFn e secondFn demoram muito tempo para serem concluídos, portanto, queremos processar essa sequência de forma assíncrona. Primeiro, criamos um novo
deferred
objeto, que representa uma cadeia de operações:A
promise
propriedade representa o resultado final da cadeia. Se você registrar uma promessa imediatamente após criá-la, verá que é apenas um objeto vazio ({}
). Nada a ver ainda, siga em frente.Até agora, nossa promessa representa apenas o ponto de partida da cadeia. Agora vamos adicionar nossas duas operações:
O
then
método adiciona uma etapa à cadeia e, em seguida, retorna uma nova promessa que representa o resultado final da cadeia estendida. Você pode adicionar quantas etapas desejar.Até agora, configuramos nossa cadeia de funções, mas nada realmente aconteceu. Você começa chamando
deferred.resolve
, especificando o valor inicial que deseja passar para a primeira etapa real da cadeia:E então ... ainda nada acontece. Para garantir que as alterações do modelo sejam observadas corretamente, o Angular não chama realmente a primeira etapa da cadeia até que a próxima vez
$apply
seja chamada:E o tratamento de erros? Até agora, especificamos apenas um manipulador de sucesso em cada etapa da cadeia.
then
também aceita um manipulador de erros como um segundo argumento opcional. Aqui está outro exemplo mais longo de uma cadeia de promessas, desta vez com tratamento de erros:Como você pode ver neste exemplo, cada manipulador da cadeia tem a oportunidade de desviar o tráfego para o próximo manipulador de erros em vez do próximo manipulador de sucesso . Na maioria dos casos, você pode ter um único manipulador de erros no final da cadeia, mas também pode ter manipuladores de erros intermediários que tentam recuperar.
Para retornar rapidamente aos seus exemplos (e às suas perguntas), vou apenas dizer que eles representam duas maneiras diferentes de adaptar a API orientada a retorno de chamada do Facebook à maneira da Angular de observar alterações de modelo. O primeiro exemplo envolve a chamada da API em uma promessa, que pode ser adicionada a um escopo e é entendida pelo sistema de modelagem da Angular. O segundo adota a abordagem de força bruta de definir o resultado do retorno de chamada diretamente no escopo e depois chamar
$scope.$digest()
para tornar o Angular ciente da mudança de uma fonte externa.Os dois exemplos não são diretamente comparáveis, porque o primeiro está faltando na etapa de login. No entanto, geralmente é desejável encapsular interações com APIs externas como esta em serviços separados e entregar os resultados aos controladores como promessas. Dessa forma, você pode manter seus controladores separados das preocupações externas e testá-los mais facilmente com serviços simulados.
fonte
then
métodos é usar$q.all
. Um tutorial rápido sobre isso pode ser encontrado aqui .$q.all
é apropriado se você precisar aguardar a conclusão de várias operações assíncronas independentes . Ele não substitui o encadeamento se cada operação depender do resultado da operação anterior.return 'firstResult'
parte parareturn $q.resolve('firstResult')
, qual será a diferença?Esta é a chave para o MVP de promessas angulares (promessa mínima viável) : http://plnkr.co/edit/QBAB0usWXc96TnxqKhuA?p=preview
Fonte:
(para aqueles com preguiça de clicar nos links)
index.html
app.js
(Eu sei que isso não resolve o seu exemplo específico do Facebook, mas acho úteis os seguintes trechos)
Via: http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/
Atualização 28 de fevereiro de 2014: A partir da versão 1.2.0, as promessas não são mais resolvidas pelos modelos. http://www.benlesh.com/2013/02/angularjs-creating-service-with-http.html
(o exemplo de plunker usa 1.1.5.)
fonte
Um adiado representa o resultado de uma operação assíncrona. Expõe uma interface que pode ser usada para sinalizar o estado e o resultado da operação que representa. Ele também fornece uma maneira de obter a instância de promessa associada.
Uma promessa fornece uma interface para interagir com o adiado relacionado e, portanto, permite que as partes interessadas tenham acesso ao estado e ao resultado da operação adiada.
Ao criar um adiado, seu estado está pendente e não tem nenhum resultado. Quando resolvemos () ou rejeitamos () o adiado, ele muda seu estado para resolvido ou rejeitado. Ainda assim, podemos obter a promessa associada imediatamente após criar um adiado e até atribuir interações com seu resultado futuro. Essas interações ocorrerão somente após o adiado rejeitado ou resolvido.
fonte
use a promessa dentro de um controlador e verifique se os dados estão disponíveis ou não
fonte