programing

약속 체인의 여러 캐치 처리

i4 2023. 8. 14. 22:27
반응형

약속 체인의 여러 캐치 처리

저는 아직 약속에 상당히 익숙하지 않고 현재 블루버드를 사용하고 있지만, 어떻게 해야 할지 잘 모르는 시나리오가 있습니다.

예를 들어 익스프레스 앱 내에 다음과 같은 약속 체인이 있습니다.

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.OperationalErrorBluebird가 이미 제공하고 있습니다.오류를 하위 분류하는 방법을 잘 모르면 알려주십시오.

다음을 위해 추가로 하위 분류합니다.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여기에 표시된 것처럼:

Mozilla 개발자 네트워크 - 약속 사용

편집: 일반적으로 요청하신 기능을 제공하기 때문에 제출하였습니다.하지만 이 경우에는 그렇지 않습니다.이미 했듯이, 왜냐하면이다사자설세명이듯했히이람들른미,이▁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

반응형