나는 아직 약속에 상당히 익숙하지 않고 현재 블루 버드를 사용하고 있지만 어떻게 최선을 다해야할지 잘 모르겠습니다.
예를 들어 다음과 같은 익스프레스 앱 내에 약속 체인이 있습니다.
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
그러나 나중에 처리기가 오류를 포착하기 때문에 이것만으로는 귀하의 경우에 작동하지 않습니다. 여기서 진짜 문제는 일반화 된 “아무것도 처리”오류 처리기는 일반적으로 나쁜 관행이며 다른 프로그래밍 언어와 생태계에서 극도로 눈살을 찌푸린다는 것입니다. 이러한 이유로 Bluebird는 형식화 및 술어 catch를 제공합니다.
추가 된 이점은 비즈니스 로직이 요청 / 응답주기를 전혀 인식하지 않아도된다는 것입니다. 클라이언트에 어떤 HTTP 상태와 오류가 발생하는지 결정하는 것은 쿼리의 책임이 아니며 나중에 앱이 커짐에 따라 클라이언트에 보내는 내용과 비즈니스 논리 (DB 쿼리 방법 및 데이터 처리 방법)를 분리 할 수 있습니다. (어떤 http 상태 코드, 텍스트 및 응답).
다음은 코드를 작성하는 방법입니다.
먼저, 나는 Bluebird가 이미 제공하는 하위 클래스 .Query
를 던질 NoSuchAccountError
것 Promise.OperationalError
입니다. 오류를 하위 분류하는 방법을 잘 모르겠 으면 알려주세요.
추가로 하위 클래스를 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
문장 처럼 작동합니다 . 즉, 마지막에 하나의 캐치 만 필요합니다.
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" });
}
});
답변
오류를 기반으로 특정 지점에서 체인을 강제로 중지시키는 방법이 있는지 궁금합니다.
아니요. 끝까지 버블 링되는 예외를 throw하지 않는 한 체인을 실제로 “종료”할 수 없습니다. 이를 수행하는 방법 은 Benjamin Gruenbaum의 답변 을 참조하십시오 .
그의 패턴의 파생은 오류 유형을 구별하는 것이 아니라 단일 일반 처리기 에서 보낼 수있는 statusCode
및 body
필드가있는 오류를 사용하는 것 .catch
입니다. 응용 프로그램 구조에 따라 그의 솔루션이 더 깨끗할 수 있습니다.
또는 어떤 형태의 분기 동작을 얻기 위해 이것을 구조화하는 더 좋은 방법이 있다면
예, promise를 사용하여 분기를 수행 할 수 있습니다 . 그러나 이것은 중첩 된 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
과 같이 중첩이 가능합니다 .
편집 : 일반적으로 요청 된 기능을 제공하기 때문에 제출했습니다. 그러나이 특별한 경우에는 그렇지 않습니다. 이미 다른 사람들이 자세히 설명했듯이 .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" });
}
}
);
참고 : (가) if (error != null)
해킹의 비트가 가장 최근의 오류와 상호 작용하는 것입니다.
답변
위의 Benjamin Gruenbaum의 대답 은 복잡한 논리 시퀀스에 대한 최상의 솔루션 이라고 생각 하지만 여기에 더 간단한 상황에 대한 대안이 있습니다. 다음 또는 문 을 건너 뛰기 위해 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
우리가 건너 뛸 후속 이 없기 때문이며 Node가 좋아하지 않는 처리되지 않은 Promise 거부로 간주됩니다. 위에 쓰여진 것처럼 결승전 catch
은 평화롭게 해결 된 약속을 반환합니다.