약속 체인을 끊고 체인이 끊어진 (거부 된) 단계에 따라 함수를 호출하십시오.
최신 정보:
이 게시물의 향후 시청자를 돕기 위해 나는 pluma 's answer 데모를 만들었습니다 .
질문:
나의 목표는 매우 간단 해 보인다.
step(1)
.then(function() {
return step(2);
}, function() {
stepError(1);
return $q.reject();
})
.then(function() {
}, function() {
stepError(2);
});
function step(n) {
var deferred = $q.defer();
//fail on step 1
(n === 1) ? deferred.reject() : deferred.resolve();
return deferred.promise;
}
function stepError(n) {
console.log(n);
}
여기서 문제는 1 단계에서 실패하면 stepError(1)
AND stepError(2)
가 모두 발생한다는 것 입니다. 내가하지 않으면 return $q.reject
다음 stepError(2)
발사,하지만하지 않습니다 step(2)
이해하는 것입니다. 내가하려는 일을 제외하고 모든 것을 성취했습니다.
오류 체인의 모든 함수를 호출하지 않고 거부시 함수를 호출 할 수 있도록 약속을 작성하는 방법은 무엇입니까? 아니면 이것을 달성하는 다른 방법이 있습니까?
다음은 실제 데모 입니다.
최신 정보:
나는 가지 를 해결했다. 여기서는 체인 끝에서 오류를 포착하고 데이터를 전달 reject(data)
하여 오류 기능에서 처리해야 할 문제를 알 수 있습니다. 데이터에 의존하고 싶지 않기 때문에 실제로 요구 사항을 충족시키지 못합니다. 그것은 절름발이이지만 내 경우에는 수행 할 작업을 결정하기 위해 반환 된 데이터에 의존하지 않고 함수에 오류 콜백을 전달하는 것이 더 깨끗합니다.
step(1)
.then(function() {
return step(2);
})
.then(function() {
return step(3);
})
.then(false,
function(x) {
stepError(x);
}
);
function step(n) {
console.log('Step '+n);
var deferred = $q.defer();
(n === 1) ? deferred.reject(n) : deferred.resolve(n);
return deferred.promise;
}
function stepError(n) {
console.log('Error '+n);
}
코드가 예상대로 작동하지 않는 이유는 실제로 생각한 것과 다른 것을 수행하기 때문입니다.
다음과 같은 것이 있다고 가정 해 봅시다.
stepOne()
.then(stepTwo, handleErrorOne)
.then(stepThree, handleErrorTwo)
.then(null, handleErrorThree);
무슨 일이 일어나고 있는지 더 잘 이해하기 위해 try
/ catch
블록 이있는 동기 코드 인 척하십시오 .
try {
try {
try {
var a = stepOne();
} catch(e1) {
a = handleErrorOne(e1);
}
var b = stepTwo(a);
} catch(e2) {
b = handleErrorTwo(e2);
}
var c = stepThree(b);
} catch(e3) {
c = handleErrorThree(e3);
}
onRejected
핸들러 (두 번째 인수 then
) 본질적으로 (a 같은 에러 정정 메커니즘 catch
블록). 에 오류가 발생 handleErrorOne
하면 다음 catch 블록 ( catch(e2)
) 등에서 오류가 발생 합니다.
이것은 분명히 당신이 의도 한 것이 아닙니다.
무슨 일이 있어도 전체 해결 체인이 실패하기를 원한다고 가정 해 봅시다.
stepOne()
.then(function(a) {
return stepTwo(a).then(null, handleErrorTwo);
}, handleErrorOne)
.then(function(b) {
return stepThree(b).then(null, handleErrorThree);
});
참고 : 거부 handleErrorOne
된 경우에만 호출되기 때문에 현재 위치 를 떠날 수 있습니다 stepOne
(체인의 첫 번째 기능 이므로이 시점에서 체인이 거부되면 해당 기능의 약속 때문일 수 있음을 알고 있습니다) .
중요한 변화는 다른 함수에 대한 오류 처리기가 주요 약속 체인의 일부가 아니라는 것입니다. 대신, 각 단계에는 onRejected
단계가 거부 된 경우에만 호출되는 고유 한 "서브 체인"이 있습니다 (하지만 주 체인에 직접 도달 할 수는 없음).
이 작품 이유는 둘이다 onFulfilled
하고 onRejected
받는 선택적 인수있는 then
방법은. 약속이 이행되고 (즉, 해결됨) then
체인 의 다음 약속 에 onFulfilled
처리기 가없는 경우 해당 처리기가있는 체인이있을 때까지 체인이 계속됩니다.
이는 다음 두 줄이 동일 함을 의미합니다.
stepOne().then(stepTwo, handleErrorOne)
stepOne().then(null, handleErrorOne).then(stepTwo)
그러나 다음 줄은 위의 두 줄과 동일 하지 않습니다 .
stepOne().then(stepTwo).then(null, handleErrorOne)
Angular의 약속 라이브러리 $q
는 kriskowal의 Q
라이브러리 (더 풍부한 API를 가지고 있지만에서 찾을 수있는 모든 것을 포함 $q
)를 기반으로합니다. GitHub 의 Q API 문서 가 유용 할 수 있습니다. Q는 Promises / A + spec을 구현합니다. Promises / A + 사양then
은 약속 해결 동작이 정확히 어떻게 작동 하는지에 대해 자세히 설명 합니다.
편집하다:
또한 오류 처리기에서 체인을 벗어나려면 거부 된 약속을 반환하거나 오류를 throw해야합니다 (거부 된 약속에 자동으로 잡히고 랩됩니다). 약속을 반환하지 않으면 반환 then
값을 해결 약속으로 래핑합니다.
즉, 아무 것도 반환하지 않으면 가치에 대한 해결 된 약속을 효과적으로 반환하는 것 undefined
입니다.
파티에 늦었지만이 간단한 솔루션이 나를 위해 일했습니다.
function chainError(err) {
return Promise.reject(err)
};
stepOne()
.then(stepTwo, chainError)
.then(stepThreee, chainError);
이를 통해 체인 에서 벗어날 수 있습니다 .
필요한 것은 .then()
특별한 케이스를 시작하고 특별한 케이스를 마무리 하는 반복 체인입니다.
요점은 실패 사례의 단계 번호를 최종 오류 처리기로 리플하는 것입니다.
- 시작 :
step(1)
무조건 호출 합니다. - 반복 패턴 :
.then()
다음 콜백으로 체인 a 를 연결하십시오.- 성공 : 호출 단계 (n + 1)
- 실패 : 이전 지연된 값이 거부 된 값을 던지거나 오류를 다시 발생시킵니다.
- 완료 :
.then()
성공 처리기와 최종 오류 처리기가없는 체인 a
모든 것을 손으로 쓸 수는 있지만 명명 된 일반 함수를 사용하여 패턴을 쉽게 설명 할 수 있습니다.
function nextStep(n) {
return step(n + 1);
}
function step(n) {
console.log('step ' + n);
var deferred = $q.defer();
(n === 3) ? deferred.reject(n) : deferred.resolve(n);
return deferred.promise;
}
function stepError(n) {
throw(n);
}
function finalError(n) {
console.log('finalError ' + n);
}
step(1)
.then(nextStep, stepError)
.then(nextStep, stepError)
.then(nextStep, stepError)
.then(nextStep, stepError)
.then(nextStep, stepError)
.then(null, finalError);});
참조 데모
step()
에서 지연된 항목이 거부되거나 해결되어 체인 n
에서 다음 값의 콜백에 해당 값을 사용할 수있는 방법에 유의하십시오 .then()
. stepError
이 호출 되면 에 의해 처리 될 때까지 오류가 반복해서 다시 발생합니다 finalError
.
거부 할 때 거부 오류를 전달 해야하는 경우 체인이 끝날 때까지 거부를 처리하거나 "다시 던져야"하는지 확인하는 함수에서 단계 오류 처리기를 래핑하십시오.
// function mocking steps
function step(i) {
i++;
console.log('step', i);
return q.resolve(i);
}
// function mocking a failing step
function failingStep(i) {
i++;
console.log('step '+ i + ' (will fail)');
var e = new Error('Failed on step ' + i);
e.step = i;
return q.reject(e);
}
// error handler
function handleError(e){
if (error.breakChain) {
// handleError has already been called on this error
// (see code bellow)
log('errorHandler: skip handling');
return q.reject(error);
}
// firs time this error is past to the handler
console.error('errorHandler: caught error ' + error.message);
// process the error
// ...
//
error.breakChain = true;
return q.reject(error);
}
// run the steps, will fail on step 4
// and not run step 5 and 6
// note that handleError of step 5 will be called
// but since we use that error.breakChain boolean
// no processing will happen and the error will
// continue through the rejection path until done(,)
step(0) // 1
.catch(handleError)
.then(step) // 2
.catch(handleError)
.then(step) // 3
.catch(handleError)
.then(failingStep) // 4 fail
.catch(handleError)
.then(step) // 5
.catch(handleError)
.then(step) // 6
.catch(handleError)
.done(function(){
log('success arguments', arguments);
}, function (error) {
log('Done, chain broke at step ' + error.step);
});
콘솔에 표시되는 내용 :
step 1
step 2
step 3
step 4 (will fail)
errorHandler: caught error 'Failed on step 4'
errorHandler: skip handling
errorHandler: skip handling
Done, chain broke at step 4
다음은 작동 코드입니다 https://jsfiddle.net/8hzg5s7m/3/
각 단계마다 특정 처리가있는 경우 래퍼는 다음과 같습니다.
/*
* simple wrapper to check if rejection
* has already been handled
* @param function real error handler
*/
function createHandler(realHandler) {
return function(error) {
if (error.breakChain) {
return q.reject(error);
}
realHandler(error);
error.breakChain = true;
return q.reject(error);
}
}
그럼 네 체인
step1()
.catch(createHandler(handleError1Fn))
.then(step2)
.catch(createHandler(handleError2Fn))
.then(step3)
.catch(createHandler(handleError3Fn))
.done(function(){
log('success');
}, function (error) {
log('Done, chain broke at step ' + error.step);
});
올바르게 이해하면 실패한 단계의 오류 만 표시하고 싶습니까?
첫 번째 약속의 실패 사례를 변경하는 것만 큼 간단합니다.
step(1).then(function (response) {
step(2);
}, function (response) {
stepError(1);
return response;
}).then( ... )
$q.reject()
첫 번째 단계의 실패 사례 로 돌아 가면 해당 약속을 거부하여 errorCallback이 2nd에서 호출됩니다 then(...)
.
var s = 1;
start()
.then(function(){
return step(s++);
})
.then(function() {
return step(s++);
})
.then(function() {
return step(s++);
})
.then(0, function(e){
console.log(s-1);
});
http://jsbin.com/EpaZIsIp/20/edit
또는 여러 단계에 대해 자동화하십시오.
var promise = start();
var s = 1;
var l = 3;
while(l--) {
promise = promise.then(function() {
return step(s++);
});
}
promise.then(0, function(e){
console.log(s-1);
});
http://jsbin.com/EpaZIsIp/21/edit
async / await를 사용하여이 문제를 해결하려면 다음을 수행하십시오.
(async function(){
try {
const response1, response2, response3
response1 = await promise1()
if(response1){
response2 = await promise2()
}
if(response2){
response3 = await promise3()
}
return [response1, response2, response3]
} catch (error) {
return []
}
})()
단계 실행에 오류 처리기를 별도의 체인 요소로 직접 첨부하십시오.
// Handle errors for step(1)
step(1).then(null, function() { stepError(1); return $q.reject(); })
.then(function() {
// Attach error handler for step(2),
// but only if step(2) is actually executed
return step(2).then(null, function() { stepError(2); return $q.reject(); });
})
.then(function() {
// Attach error handler for step(3),
// but only if step(3) is actually executed
return step(3).then(null, function() { stepError(3); return $q.reject(); });
});
또는 사용 catch()
:
// Handle errors for step(1)
step(1).catch(function() { stepError(1); return $q.reject(); })
.then(function() {
// Attach error handler for step(2),
// but only if step(2) is actually executed
return step(2).catch(function() { stepError(2); return $q.reject(); });
})
.then(function() {
// Attach error handler for step(3),
// but only if step(3) is actually executed
return step(3).catch(function() { stepError(3); return $q.reject(); });
});
참고 : 이것은 기본적으로 pluma가 대답에서 제안 하지만 OP의 이름 지정을 사용하는 것과 동일한 패턴 입니다.
찾을 수 Promise.prototype.catch()
MDN에 대한 예제 매우 도움이 아래.
(The accepted answer mentions then(null, onErrorHandler)
which is basically the same as catch(onErrorHandler)
.)
Using and chaining the catch method
var p1 = new Promise(function(resolve, reject) { resolve('Success'); }); p1.then(function(value) { console.log(value); // "Success!" throw 'oh, no!'; }).catch(function(e) { console.log(e); // "oh, no!" }).then(function(){ console.log('after a catch the chain is restored'); }, function () { console.log('Not fired due to the catch'); }); // The following behaves the same as above p1.then(function(value) { console.log(value); // "Success!" return Promise.reject('oh, no!'); }).catch(function(e) { console.log(e); // "oh, no!" }).then(function(){ console.log('after a catch the chain is restored'); }, function () { console.log('Not fired due to the catch'); });
Gotchas when throwing errors
// Throwing an error will call the catch method most of the time var p1 = new Promise(function(resolve, reject) { throw 'Uh-oh!'; }); p1.catch(function(e) { console.log(e); // "Uh-oh!" }); // Errors thrown inside asynchronous functions will act like uncaught errors var p2 = new Promise(function(resolve, reject) { setTimeout(function() { throw 'Uncaught Exception!'; }, 1000); }); p2.catch(function(e) { console.log(e); // This is never called }); // Errors thrown after resolve is called will be silenced var p3 = new Promise(function(resolve, reject) { resolve(); throw 'Silenced Exception!'; }); p3.catch(function(e) { console.log(e); // This is never called });
If it is resolved
//Create a promise which would not call onReject var p1 = Promise.resolve("calling next"); var p2 = p1.catch(function (reason) { //This is never called console.log("catch p1!"); console.log(reason); }); p2.then(function (value) { console.log("next promise's onFulfilled"); /* next promise's onFulfilled */ console.log(value); /* calling next */ }, function (reason) { console.log("next promise's onRejected"); console.log(reason); });
The best solution is to refactor to your promise chain to use ES6 await's. Then you can just return from the function to skip the rest of the behavior.
I have been hitting my head against this pattern for over a year and using await's is heaven.
Try ro use this like libs:
https://www.npmjs.com/package/promise-chain-break
db.getData()
.then(pb((data) => {
if (!data.someCheck()) {
tellSomeone();
// All other '.then' calls will be skiped
return pb.BREAK;
}
}))
.then(pb(() => {
}))
.then(pb(() => {
}))
.catch((error) => {
console.error(error);
});
'Programing' 카테고리의 다른 글
선택한 특정 열을 새 DataFrame에 복사본으로 추출 (0) | 2020.07.07 |
---|---|
'eval'을 사용하는 것은 왜 나쁜 습관입니까? (0) | 2020.07.07 |
소켓 라이브러리에서 recv를 호출 할 때 recv 버퍼의 크기는 얼마입니까? (0) | 2020.07.07 |
Json Array를 일반 Java 목록으로 변환 (0) | 2020.07.07 |
각도 2에서 돔 요소를 얻는 방법 (0) | 2020.07.07 |