약속 체인의 여러 캐치 처리
저는 아직 약속에 상당히 익숙하지 않고 현재 블루버드를 사용하고 있지만, 어떻게 해야 할지 잘 모르는 시나리오가 있습니다.
예를 들어 익스프레스 앱 내에 다음과 같은 약속 체인이 있습니다.
repository.Query(getAccountByIdQuery)
.catch(function(error){
res.status(404).send({ error: "No account found with this Id" });
})
.then(convertDocumentToModel)
.then(verifyOldPassword)
.catch(function(error) {
res.status(406).send({ OldPassword: error });
})
.then(changePassword)
.then(function(){
res.status(200).send();
})
.catch(function(error){
console.log(error);
res.status(500).send({ error: "Unable to change password" });
});
그래서 제가 추구하는 행동은 다음과 같습니다.
- ID로 계정을 가져옵니다.
- 이 시점에서 거부가 발생하면 폭탄을 제거하고 오류를 반환합니다.
- 오류가 없으면 반환된 문서를 모델로 변환합니다.
- 데이터베이스 문서로 비밀번호 확인
- 암호가 일치하지 않으면 폭탄을 터뜨리고 다른 오류를 반환합니다.
- 오류가 없으면 암호를 변경합니다.
- 그러면 성공을 반환합니다.
- 다른 문제가 생기면 500달러를 돌려주세요.
것 이 됩니다.로 멈추도록 , 위해 하는 더 . 그래서 저는 오류를 기반으로 체인을 특정 지점에서 강제로 멈추는 방법이 있는지, 아니면 어떤 형태로든 분기 동작을 얻기 위해 이것을 구조화할 수 있는 더 나은 방법이 있는지 궁금합니다.if X do Y else Z
.
어떤 도움이든 좋습니다.
이 동작은 정확히 동기식 던지기와 같습니다.
try{
throw new Error();
} catch(e){
// handle
}
// this code will run, since you recovered from the error!
그것이 요점의 절반입니다..catch
오류를 복구할 수 있습니다.상태가 여전히 오류임을 알리기 위해 다시 던지는 것이 바람직할 수 있습니다.
try{
throw new Error();
} catch(e){
// handle
throw e; // or a wrapper over e so we know it wasn't handled
}
// this code will not run
그러나 나중에 오류가 발생한 핸들러에 의해 감지되기 때문에 이 경우에만 작동하지 않습니다.여기서 진짜 문제는 일반화된 "Handle Anything" 오류 처리기는 일반적으로 나쁜 관행이며 다른 프로그래밍 언어와 생태계에서 극도로 눈살을 찌푸리게 한다는 것입니다.이러한 이유로 Bluebird는 유형 및 술어 캐치를 제공합니다.
추가적인 장점은 비즈니스 로직이 요청/응답 주기를 전혀 인식하지 않아도 된다는 것입니다.클라이언트가 어떤 HTTP 상태와 오류를 얻는지 결정하는 것은 쿼리의 책임이 아닙니다. 나중에 앱이 커짐에 따라 비즈니스 로직(DB 쿼리 방법 및 데이터 처리 방법)을 클라이언트에 보내는 내용(어떤 http 상태 코드, 어떤 텍스트 및 어떤 응답)과 분리해야 할 수도 있습니다.
여기 코드를 작성하는 방법이 있습니다.
우, 나..Query
을 던지다NoSuchAccountError
로부터 하위 분류합니다.Promise.OperationalError
Bluebird가 이미 제공하고 있습니다.오류를 하위 분류하는 방법을 잘 모르면 알려주십시오.
다음을 위해 추가로 하위 분류합니다.AuthenticationError
그런 다음 다음과 같은 작업을 수행합니다.
function changePassword(queryDataEtc){
return repository.Query(getAccountByIdQuery)
.then(convertDocumentToModel)
.then(verifyOldPassword)
.then(changePassword);
}
보시는 바와 같이 매우 깨끗하고 프로세스에서 발생하는 작업에 대한 사용 설명서처럼 텍스트를 읽을 수 있습니다.또한 요청/응답과 분리됩니다.
경로 핸들러에서 다음과 같이 호출합니다.
changePassword(params)
.catch(NoSuchAccountError, function(e){
res.status(404).send({ error: "No account found with this Id" });
}).catch(AuthenticationError, function(e){
res.status(406).send({ OldPassword: error });
}).error(function(e){ // catches any remaining operational errors
res.status(500).send({ error: "Unable to change password" });
}).catch(function(e){
res.status(500).send({ error: "Unknown internal server error" });
});
이렇게 하면 논리가 모두 한 곳에 배치되고 고객에게 오류를 처리하는 방법에 대한 결정이 한 곳에 배치되어 서로를 혼란스럽게 하지 않습니다.
.catch
와 같은 일을 합니다.try-catch
즉입니다. 즉, 마에하캐나치있만됩으니면다의문지장막▁statement,됩니.
repository.Query(getAccountByIdQuery)
.then(convertDocumentToModel)
.then(verifyOldPassword)
.then(changePassword)
.then(function(){
res.status(200).send();
})
.catch(function(error) {
if (/*see if error is not found error*/) {
res.status(404).send({ error: "No account found with this Id" });
} else if (/*see if error is verification error*/) {
res.status(406).send({ OldPassword: error });
} else {
console.log(error);
res.status(500).send({ error: "Unable to change password" });
}
});
오류를 바탕으로 특정 지점에서 체인을 강제로 정지시킬 수 있는 방법이 있는지 궁금합니다.
아니요. 체인이 끝날 때까지 거품이 발생하는 예외를 던지지 않는 한 체인을 "종료"할 수 없습니다.그 방법은 벤자민 그루엔바움의 대답을 참조하십시오.
유형을 하는 것이 오류 을 사용하는 입니다.statusCode
그리고.body
인 단일 필드에서 할 수 .catch
구조에 은 더 수 그러나 애플리케이션 구조에 따라 그의 솔루션이 더 깨끗해질 수 있습니다.
또는 어떤 형태로든 분기 동작을 얻기 위해 이것을 구성하는 더 나은 방법이 있다면,
네, 당신은 약속을 가지고 가지치기를 할 수 있습니다.그러나 이는 중첩된 if-else 또는 try-catch 문에서와 같이 체인을 그대로 두고 중첩으로 "돌아가기"를 의미합니다.
repository.Query(getAccountByIdQuery)
.then(function(account) {
return convertDocumentToModel(account)
.then(verifyOldPassword)
.then(function(verification) {
return changePassword(verification)
.then(function() {
res.status(200).send();
})
}, function(verificationError) {
res.status(406).send({ OldPassword: error });
})
}, function(accountError){
res.status(404).send({ error: "No account found with this Id" });
})
.catch(function(error){
console.log(error);
res.status(500).send({ error: "Unable to change password" });
});
저는 이런 식으로 해왔습니다.
당신은 결국 당신의 포획물을 남깁니다.그리고 그것이 당신의 사슬의 중간에 발생할 때 오류를 던져라.
repository.Query(getAccountByIdQuery)
.then((resultOfQuery) => convertDocumentToModel(resultOfQuery)) //inside convertDocumentToModel() you check for empty and then throw new Error('no_account')
.then((model) => verifyOldPassword(model)) //inside convertDocumentToModel() you check for empty and then throw new Error('no_account')
.then(changePassword)
.then(function(){
res.status(200).send();
})
.catch((error) => {
if (error.name === 'no_account'){
res.status(404).send({ error: "No account found with this Id" });
} else if (error.name === 'wrong_old_password'){
res.status(406).send({ OldPassword: error });
} else {
res.status(500).send({ error: "Unable to change password" });
}
});
다른 기능은 다음과 같습니다.
function convertDocumentToModel(resultOfQuery) {
if (!resultOfQuery){
throw new Error('no_account');
} else {
return new Promise(function(resolve) {
//do stuff then resolve
resolve(model);
}
}
파티에 이지만, 둥지를 틀 도 있습니다..catch
여기에 표시된 것처럼:
편집: 일반적으로 요청하신 기능을 제공하기 때문에 제출하였습니다.하지만 이 경우에는 그렇지 않습니다.이미 했듯이, 왜냐하면이다사자설세명이듯했히이람들른미,이▁because설,.catch
오류를 복구해야 합니다.예를 들어 클라이언트에 여러 번 응답을 보낼 수 없습니다. .catch
..catch
인 골적로 return
으로 해결합니다.undefined
에 진행을 것입니다..then
체인이 실제로 해결되지 않았는데도 트리거하여 잠재적으로 다음과 같은 문제를 야기할 수 있습니다..catch
고객에게 다른 응답을 트리거하고 전송하여 오류를 유발하고 가능성이 있습니다.UnhandledPromiseRejection
당신의 방식나는 이 난해한 문장이 당신에게 어떤 의미가 있기를 바랍니다.
대신에.then().catch()...
할수있습니다.then(resolveFunc, rejectFunc)
이 약속 사슬은 당신이 도중에 일을 처리하는 것이 더 나을 것입니다.제가 다시 작성하는 방법은 다음과 같습니다.
repository.Query(getAccountByIdQuery)
.then(
convertDocumentToModel,
() => {
res.status(404).send({ error: "No account found with this Id" });
return Promise.reject(null)
}
)
.then(
verifyOldPassword,
() => Promise.reject(null)
)
.then(
changePassword,
(error) => {
if (error != null) {
res.status(406).send({ OldPassword: error });
}
return Promise.Promise.reject(null);
}
)
.then(
_ => res.status(200).send(),
error => {
if (error != null) {
console.error(error);
res.status(500).send({ error: "Unable to change password" });
}
}
);
참고: Theif (error != null)
가장 최근의 오류와 상호 작용하는 것은 약간의 해킹입니다.
저는 위의 벤자민 그루엔바움의 답변이 복잡한 논리 시퀀스에 대한 최고의 해결책이라고 생각하지만, 여기 더 간단한 상황에 대한 저의 대안이 있습니다.저는 그냥.errorEncountered
와 함께 깃발을 내미는.return Promise.reject()
후속 작업을 건너뛰다then
또는catch
진들술. 따라서 다음과 같습니다.
let errorEncountered = false;
someCall({
/* do stuff */
})
.catch({
/* handle error from someCall*/
errorEncountered = true;
return Promise.reject();
})
.then({
/* do other stuff */
/* this is skipped if the preceding catch was triggered, due to Promise.reject */
})
.catch({
if (errorEncountered) {
return;
}
/* handle error from preceding then, if it was executed */
/* if the preceding catch was executed, this is skipped due to the errorEncountered flag */
});
두 쌍 이상의 Then/Catch 쌍이 있는 경우 Benjamin Gruenbaum의 솔루션을 사용해야 합니다.그러나 이것은 간단한 설정에 효과가 있습니다.
참고로 결승전은catch
가 있을 뿐인return;
return Promise.reject();
에 오는 후조치없때문다니입이 없기 때문입니다.then
우리가 건너뛸 필요가 있고, 노드가 좋아하지 않는 처리되지 않은 약속 거부로 간주됩니다. 있는 위에쓰는것처럼있전결, 승여전▁the▁as▁final결.catch
평화적으로 해결된 약속을 반환합니다.
이 가지고 있는 도, 가 없는 Bergi의 깨끗한 코드 구조를 ..then()
의
이 의 추악함을 수 , chained와 한 클린 코드 입니다..then()
의
이와 같은 체인을 구성할 때 한 가지 좋은 점은 모든 잠재적인 결과를 한 곳에서 처리할 수 있다는 것입니다.chainRequests(...).then(handleAllPotentialResults)
이는 표준화된 인터페이스 뒤에 요청 체인을 숨겨야 할 경우 유용할 수 있습니다.
const log = console.log;
const chainRequest = (stepFunction, step) => (response) => {
if (response.status === 200) {
return stepFunction(response, step);
}
else {
log(`Failure at step: ${step}`);
return response;
}
};
const chainRequests = (initialRequest, ...steps) => {
const recurs = (step) => (response) => {
const incStep = step + 1;
const nextStep = steps.shift();
return nextStep ? nextStep(response, step).then(chainRequest(recurs(incStep), incStep)) : response;
};
return initialRequest().then(recurs(0));
};
// Usage
async function workingExample() {
return await chainRequests(
() => fetch('https://jsonplaceholder.typicode.com/users'),
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/'); },
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/3'); }
);
}
async function failureExample() {
return await chainRequests(
() => fetch('https://jsonplaceholder.typicode.com/users'),
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/fail'); },
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/3'); }
);
}
console.log(await workingExample());
console.log(await failureExample());
아이디어는 거기에 있지만, 노출된 인터페이스는 아마도 조정이 필요할 것입니다.
이 구현이 카레 화살표 기능을 사용했기 때문에 위의 구현은 잠재적으로 더 직접적으로 구현될 수 있습니다.async/await
언급URL : https://stackoverflow.com/questions/26076511/handling-multiple-catches-in-promise-chain
'programing' 카테고리의 다른 글
debian-sys-maint에 대한 권한을 재설정할 수 없습니다. 오류 1045 액세스가 거부되었습니다. (0) | 2023.08.14 |
---|---|
문자열의 특수 문자를 _(밑줄)로 바꿉니다. (0) | 2023.08.14 |
git를 반복하지 않는 두 파일을 비교하는 방법 (0) | 2023.08.14 |
php-cli에서 php.ini 다시 로드 (0) | 2023.08.14 |
프로그래밍 방식으로 'value Changes'를 실행하는 방법은 무엇입니까? (0) | 2023.08.14 |