이것은 귀하의 질문에 대한 완전한 대답은 아니지만 $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
메소드 결과에 관계없이 항상 콜백을 호출합니다 .
이제 각 단계에서 오류를 올바르게 처리하고 몇 주 후에 다른 사람이나 사용자가 읽을 수있는 방식으로 강력한 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와의 상호 작용을 별도의 서비스로 캡슐화하고 결과를 컨트롤러에 약속으로 전달하는 것이 바람직합니다. 이렇게하면 컨트롤러를 외부 문제와 분리하여 모의 서비스를 통해보다 쉽게 테스트 할 수 있습니다.