AngularJS : 약속은 어디에 사용합니까?


141

FB Graph 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의 서비스는?
  • 그리고 어떻게 작동 합니까?

9
약속이 무엇인지, 그리고 왜 일반적으로 사용되는지에 대해 읽어보아야 할 것 같습니다 ... 그것들은 각도에 국한되지 않고 많은 자료가 있습니다
charlietfl

1
@charlietfl, 좋은 지적이지만, 일반적으로 사용되는 이유와 Angular에서 사용하는 방법을 모두 다루는 복잡한 대답을 기대했습니다. 귀하의 제안을 주셔서 감사합니다
Maksym

답변:


401

이것은 귀하의 질문에 대한 완전한 대답은 아니지만 $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실패 하면 어떻게됩니까 ? 이 메소드 successFB.api메소드 결과에 관계없이 항상 콜백을 호출합니다 .

이제 각 단계에서 오류를 올바르게 처리하고 몇 주 후에 다른 사람이나 사용자가 읽을 수있는 방식으로 강력한 3 개 이상의 비동기 작업 시퀀스를 코딩하려고한다고 상상해보십시오. 가능하지만 콜백을 중첩하고 길을 따라 오류를 추적하는 것은 매우 쉽습니다.

이제 잠시 동안 Facebook API를 제외하고 $q서비스에서 구현 된대로 Angular Promises API를 고려해 봅시다 . 이 서비스에 의해 구현 된 패턴은 비동기 프로그래밍을 선형 일련의 간단한 명령문과 유사한 것으로 다시 변환하려는 시도로, 어떤 방식 으로든 오류를 '던지기'하고 결국에는이를 처리 할 수있는 능력을 갖습니다. 익숙한 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);

이 예제에서 볼 수 있듯이 체인의 각 처리기 는 다음 성공 처리기 대신 다음 오류 처리기로 트래픽을 전환 할 수 있습니다. 대부분의 경우 체인 끝에 단일 오류 처리기가있을 수 있지만 복구를 시도하는 중간 오류 처리기가있을 수도 있습니다.

예제와 질문으로 신속하게 돌아 가기 위해 Facebook의 콜백 지향 API를 Angular의 모델 변경 사항 관찰 방식에 적용하는 두 가지 방법을 나타냅니다. 첫 번째 예는 API 호출을 약속으로 감싸며, 범위에 추가 될 수 있으며 Angular의 템플릿 시스템에서 이해할 수 있습니다. 두 번째는 스코프에서 직접 콜백 결과를 설정 한 다음 $scope.$digest()Angular가 외부 소스의 변경 사항을 인식하도록 호출하는보다 무차별 접근 방식을 취합니다 .

첫 번째에는 로그인 단계가 없기 때문에 두 예제는 직접 비교할 수 없습니다. 그러나 일반적으로 이와 같은 외부 API와의 상호 작용을 별도의 서비스로 캡슐화하고 결과를 컨트롤러에 약속으로 전달하는 것이 바람직합니다. 이렇게하면 컨트롤러를 외부 문제와 분리하여 모의 서비스를 통해보다 쉽게 ​​테스트 할 수 있습니다.


5
좋은 대답이라고 생각합니다! 저에게 가장 중요한 것은 약속이 실제로 실제 일 때의 일반적인 경우를 설명하는 것이 었습니다. 솔직히 (Facebook과 같은) 또 다른 실제 예를 기대하고 있었지만 이것도 작동합니다. 많은 감사합니다!
Maksym

2
여러 then방법 을 연결하는 대신 사용할 수 있습니다 $q.all. 이에 대한 빠른 자습서는 여기 에서 찾을 수 있습니다 .
Bogdan

2
$q.all여러 개의 독립적 인 비동기 작업이 완료 될 때까지 기다려야하는 경우에 적합합니다 . 각 작업이 이전 작업의 결과에 의존하는 경우 체인을 대체하지 않습니다.
karlgold

1
그때의 연쇄는 간결하게 여기에 설명되어 있습니다. 그것을 최대한 활용하고 이해하도록 도와주었습니다. 감사합니다
Tushar Joshi

1
큰 답변 @karlgold! 질문이 하나 있습니다. 마지막 코드 스 니펫에서 return 'firstResult'파트를로 변경 return $q.resolve('firstResult')하면 차이점은 무엇입니까?
technophyle

9

나는 일반적으로 사용되는 이유와 Angular에서 사용하는 방법을 모두 다루는 복잡한 대답을 기대했습니다.

이것은 각도 약속에 대한 펑크입니다 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

(플러 커 예제는 1.1.5를 사용합니다.)


우리는 게으
르기

이것은 $ q, deferred and chained .then 함수 호출을 이해하는 데 도움이되었습니다. 감사합니다.
aliopi

1

지연은 비동기 작업의 결과를 나타냅니다. 상태 및 상태를 나타내는 작업 결과를 시그널링하는 데 사용할 수있는 인터페이스를 제공합니다. 또한 연관된 ​​promise 인스턴스를 얻는 방법을 제공합니다.

약속은 지연된 관련 항목과 상호 작용하기위한 인터페이스를 제공하므로 관심있는 당사자가 상태 및 지연된 작업 결과에 액세스 할 수 있습니다.

지연을 만들 때 상태는 보류 중이며 결과가 없습니다. 지연된 사람을 resolve () 또는 reject ()하면 상태를 해결 또는 거부 됨으로 변경합니다. 그럼에도 불구하고 지연된 연기를 생성 한 후 즉시 관련 약속을 얻을 수 있으며 향후 결과와 상호 작용을 할당 할 수도 있습니다. 이러한 상호 작용은 지연된 거부 또는 해결 후에 만 ​​발생합니다.


1

컨트롤러 내에서 약속을 사용하고 데이터를 사용할 수 있는지 확인하십시오

 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>

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.