오류가 발생한 경우에만 약속을 거부합니까?


25

약속을 반환하는이 기능을 인증한다고 가정 해 봅시다. 그런 다음 약속은 결과로 해결됩니다. 내가 본 것처럼 False와 true는 예상되는 결과이며 거부는 오류가 발생한 경우에만 발생합니다. 또는 인증 실패가 약속을 거부하는 것으로 간주됩니까?


인증이 실패하면, 당신은해야 reject하고 false를 반환하지,하지만 당신은을로 값을 예상하는 경우 Bool에, 당신은 있었다 성공과 상관없이 값의 BOOL으로 해결해야한다. 약속은 값에 대한 일종의 프록시입니다. 반환 된 값을 저장하므로 값을 얻을 수없는 경우에만 반환됩니다 reject. 그렇지 않으면해야합니다 resolve.

좋은 질문입니다. 약속 디자인의 실패 중 하나에 대해 설명합니다. 사용자가 잘못된 입력을 제공 할 때 (예 : 로그인 실패) 예상치 못한 오류와 코드의 버그 인 예상치 못한 오류의 두 가지 유형의 오류가 있습니다. 약속 디자인은 두 개념을 단일 흐름으로 병합하여 두 개념을 구별하기 어렵게 만듭니다.
zzzzBov

1
나는 말할 것 해결 수단이 응답을 사용하여 응용 프로그램을 계속하면서 거부 (다시 시도하거나 다른 무언가를 가능하게하고) 현재 작업을 취소하는 방법을.

4
다른 방법으로 생각하십시오-동기식 메소드 호출 인 경우 정기적 인 인증 실패 (잘못된 사용자 이름 / 암호)를 반환 false또는 예외로 처리 하시겠습니까?
wrschneider

2
가져 오기 API는 이것의 좋은 예입니다. then오류 코드가 반환 되더라도 서버가 응답 할 때 항상 트리거 되므로를 확인해야합니다 response.ok. catch핸들러에 대해서만 트리거됩니다 예상치 못한 오류가 발생합니다.
CodingIntrigue

답변:


22

좋은 질문! 확실한 답은 없습니다. 흐름의 특정 지점 에서 예외적 이라고 생각하는 대상에 따라 다릅니다 .

a를 거부하는 Promise것은 예외를 발생시키는 것과 같습니다. 모든 원치 않는 결과가 예외적 인 것은 아니며 오류 의 결과입니다 . 당신은 두 가지 방법으로 당신의 사건을 논쟁 할 수 있습니다 :

  1. 실패 인증해야 , 호출자가 예상되기 때문에 대가로 개체를 다른 어떤이 흐름에 예외입니다.rejectPromiseUser

  2. 실패 인증해야 에 불구하고 잘못된 자격 증명을 제공하는 것이 정말하지 않기 때문에, 예외적 인 경우, 호출자는 항상에 흐름이 발생할 기 대해서는 안된다 .resolvePromisenullUser

발신자 측에서 문제 보고 있습니다 . 정보 흐름에서 호출자는 자신의 동작으로 인해 오류가 발생하거나 다른 오류가 발생 한다고 예상User 합니까? 아니면이 특정 호출자가 다른 결과를 처리하는 것이 합리적입니까?

멀티 레이어 시스템에서 데이터가 레이어를 통과 할 때 응답이 변경 될 수 있습니다. 예를 들면 다음과 같습니다.

  • HTTP 레이어에 RESOLVE! 요청이 전송되었고 소켓이 완전히 닫히고 서버가 유효한 응답을 생성했습니다. 가져 오기 API는 이 작업을 수행합니다.
  • 그런 다음 프로토콜 계층은 거부합니다. 응답의 상태 코드는 401이며 HTTP에는 적합하지만 프로토콜에는 적합하지 않습니다!
  • 인증 계층에 NO, RESOLVE! 401은 잘못된 비밀번호에 대한 예상 상태이므로 사용자에게 확인되므로 오류를 포착합니다 null.
  • 인터페이스 컨트롤러에 NONE OF THAT, REJECT! 화면에 표시되는 모달은 사용자 이름과 아바타를 기대하고 있었으며 해당 정보 이외의 것은 오류입니다.

이 4 점 예제는 분명히 복잡하지만 2 점을 보여줍니다.

  1. 예외 / 거부 여부는 주변 흐름과 기대에 따라 달라집니다
  2. 프로그램의 다른 계층은 다양한 흐름 단계에 있으므로 동일한 결과를 다르게 처리 할 수 ​​있습니다.

다시 한번, 대답은 없습니다. 생각하고 디자인 할 시간!


6

따라서 약속은 JS를 기능적 언어에서 가져 오는 좋은 속성을 가지고 있습니다. 즉 , 논리가 한 분기 또는 다른 분기를 취하도록 강제하여 Either두 가지 다른 유형, Left유형 및 유형을 결합하는 이 유형 생성자를 실제로 구현한다는 것입니다. Right분기.

data Either x y = Left x | Right y

이제 왼쪽의 유형이 약속에 대해 모호하다는 것을 알았습니다. 당신은 아무것도 거부 할 수 있습니다. JS가 약한 유형이기 때문에 이것은 사실 이지만, 방어 적으로 프로그래밍하는 경우 조심해야 합니다.

그 이유는 JS가 throw약속 처리 코드에서 명령문을 가져 와서 Left측면 으로 묶기 때문 입니다. 기술적으로 JS에서 당신은 할 수있는 throw참 /의 허위 또는 문자열이나 숫자를 포함, 모든 것을 :하지만 자바 스크립트 코드도없이 일을 던졌습니다throw (당신이 널 (null)에 대한 속성에 액세스하려고하는 등의 작업을 수행 할 경우) 및 이에 대한 해결 API합니다 (이 Error개체) . 따라서 잡으려고 할 때 일반적으로 이러한 오류가 Error객체 라고 가정하는 것이 좋습니다 . 그리고 reject약속은 위의 버그 중 하나에서 발생하는 모든 오류로 인해 집계 되기 때문에 일반적으로 throw다른 오류 만 원하므로 catch명령문이 단순하고 일관된 논리를 갖도록해야합니다.

따라서 조건부 조건을 입력 하고 잘못된 오류를 찾을 있지만 catch이 경우에는 진실이 사소한 것입니다.

Either (Either Error ()) ()

최소한 인증 자 에서 즉시 나오는 것보다 간단한 부울 의 논리 구조를 선호 할 것입니다 .

Either Error Bool

실제로 다음 단계의 인증 로직은 아마도 User인증 된 사용자를 포함하는 일종의 객체 를 반환하는 것입니다 .

Either Error (Maybe User)

그리고 이것은 다소 기대하는 것입니다 : null사용자가 정의되지 않은 경우 return, 그렇지 않으면 return {user_id: <number>, permission_to_launch_missiles: <boolean>}. 나는 우리가 "새로운 고객에게 데모"모드의 일종에 있다면 로그인하지의 일반적인 경우 예를 들어, 복구 가능한 것으로 기대하고 실수로 불리는 버그에 혼합해서는 안됩니다 object.doStuff()object.doStuff이었다 undefined.

이제 말했듯이, 당신이하고 싶은 것은에서 파생 된 NotLoggedIn또는 PermissionError예외 를 정의하는 것입니다 Error. 그런 다음 정말로 필요한 것들에 작성하고 싶습니다.

function launchMissiles() {
    function actuallyLaunchThem() {
        // stub
    }
    return getAuth().then(auth => {
        if (auth === null) {
            throw new PermissionError('Cannot launch missiles without permission, cannot have permission if not logged in.');
        } else if (auth.permission_to_launch_missiles) {
            return actuallyLaunchThem();
        } else {
            throw new PermissionError(`User ${auth.user_id} does not have permission to launch the missiles.`);
        }
    });
}

3

오류

오류에 대해 이야기합시다.

두 가지 유형의 오류가 있습니다.

  • 예상되는 오류
  • 예기치 않은 오류
  • 일대일 오류

예상되는 오류

예상되는 오류는 잘못된 일이 발생하는 상태이지만 오류가 발생할 수 있음을 알고 있으므로 처리해야합니다.

이들은 사용자 입력 또는 서버 요청과 같은 것입니다. 사용자가 실수를하거나 서버가 다운 될 수 있음을 알고 있으므로 프로그램이 다시 입력을 요청하거나 메시지를 표시하거나 기타 적절한 동작이 있는지 확인하기 위해 검사 코드를 작성합니다.

처리시 복구 가능합니다. 처리하지 않으면 예기치 않은 오류가 발생합니다.

예기치 않은 오류

예기치 않은 오류 (버그)는 코드가 잘못되어 잘못된 일이 발생한 상태입니다. 당신은 그들이 결국 일어날 것이라는 것을 알고 있지만, 정의에 따르면, 그것들은 예상치 못한 것이기 때문에 그것들을 어디서 어떻게 다루어야하는지 알 수있는 방법이 없습니다.

이들은 구문 및 논리 오류와 같은 것입니다. 코드에 오타가 있거나 매개 변수가 잘못된 함수를 호출했을 수 있습니다. 이들은 일반적으로 복구 할 수 없습니다.

try..catch

에 대해 이야기합시다 try..catch.

JavaScript에서는 throw일반적으로 사용되지 않습니다. 코드에서 예제를 살펴보면 거의 사이에 없으며 일반적으로 라인을 따라 구조화됩니다.

function example(param) {
  if (!Array.isArray(param) {
    throw new TypeError('"param" should be an array!');
  }
  ...
}

이 때문에 try..catch블록이 제어 흐름에 공통적 인 것은 아닙니다. 일반적으로 예상되는 오류를 피하기 위해 메소드를 호출하기 전에 몇 가지 검사를 추가하는 것이 매우 쉽습니다.

JavaScript 환경도 매우 관대하므로 예기치 않은 오류도 종종 포착되지 않습니다.

try..catch드문 일이 아닙니다. Java 및 C #과 같은 언어에서 일반적으로 사용되는 유용한 사용 사례가 있습니다. Java와 C #은 형식화 된 catch구문을 사용하므로 예상되는 오류와 예기치 않은 오류를 구분할 수 있습니다.

C # :
try
{
  var example = DoSomething();
}
catch (ExpectedException e)
{
  DoSomethingElse(e);
}

이 예에서는 다른 예기치 않은 예외가 발생하여 다른 곳에서 처리 될 수 있습니다 (예 : 프로그램을 로그하고 닫는 등).

JavaScript에서이 구문은 다음을 통해 복제 할 수 있습니다.

try {
  let example = doSomething();
} catch (e) {
  if (e instanceOf ExpectedError) {
    DoSomethingElse(e);
  } else {
    throw e;
  }
}

우아하지는 않습니다. 이것이 드문 이유의 일부입니다.

기능

기능에 대해 이야기합시다.

단일 책임 원칙 을 사용하는 경우 각 클래스와 기능은 단일 목적으로 사용되어야합니다.

예를 들어 authenticate()사용자를 인증 할 수 있습니다.

이것은 다음과 같이 작성 될 수 있습니다.

const user = authenticate();
if (user == null) {
  // keep doing stuff
} else {
  // handle expected error
}

또는 다음과 같이 작성 될 수 있습니다.

try {
  const user = authenticate();
  // keep doing stuff
} catch (e) {
  if (e instanceOf AuthenticationError) {
    // handle expected error
  } else {
    throw e;
  }
}

둘 다 허용됩니다.

약속

약속에 대해 이야기합시다.

약속은의 비동기 형식입니다 try..catch. 코드를 호출 new Promise하거나 Promise.resolve시작합니다 try. 전화 throw하거나 코드로 Promise.reject보냅니다 catch.

Promise.resolve(value)   // try
  .then(doSomething)     // try
  .then(doSomethingElse) // try
  .catch(handleError)    // catch

사용자를 인증하기위한 비동기 함수가있는 경우 다음과 같이 작성할 수 있습니다.

authenticate()
  .then((user) => {
    if (user == null) {
      // keep doing stuff
    } else {
      // handle expected error
    }
  });

또는 다음과 같이 작성 될 수 있습니다.

authenticate()
  .then((user) => {
    // keep doing stuff
  })
  .catch((e) => {
    if (e instanceOf AuthenticationError) {
      // handle expected error
    } else {
      throw e;
    }
  });

둘 다 허용됩니다.

중첩

중첩에 대해 이야기합시다.

try..catch중첩 될 수 있습니다. 귀하의 authenticate()방법은 내부적으로있을 수 있습니다 try..catch블록 등을 :

try {
  const credentials = requestCredentialsFromUser();
  const user = getUserFromServer(credentials);
} catch (e) {
  if (e instanceOf CredentialsError) {
    // handle failure to request credentials
  } else if (e instanceOf ServerError) {
    // handle failure to get data from server
  } else {
    throw e; // no idea what happened
  }
}

마찬가지로 약속은 중첩 될 수 있습니다. 비동기 authenticate()메소드는 내부적으로 약속을 사용할 수 있습니다.

requestCredentialsFromUser()
  .then(getUserFromServer)
  .catch((e) => {
    if (e instanceOf CredentialsError) {
      // handle failure to request credentials
    } else if (e instanceOf ServerError) {
      // handle failure to get data from server
    } else {
      throw e; // no idea what happened
    }
  });

그래서 대답은 무엇입니까?

좋아, 나는 실제로 질문에 대답 할 때가되었다고 생각한다.

인증 실패가 약속을 거부하는 것으로 간주됩니까?

내가 줄 수있는 가장 간단한 대답 throw은 동기 코드 인 경우 예외를 원할 경우 약속을 거부해야한다는 것 입니다.

명세서 if에서 몇 가지 점검을 통해 제어 흐름이 더 단순 then하다면 약속을 거부 할 필요가 없습니다.

약속을 거부하고 오류 처리 코드에서 오류 유형을 확인하여 제어 흐름이 더 간단한 경우 대신 처리하십시오.


0

Promise의 "거부"분기를 사용하여 jQuery UI 대화 상자의 "취소"작업을 나타 냈습니다. 대화 상자에는 종종 "닫기"옵션이 여러 개 있기 때문에 "해결"분기를 사용하는 것보다 자연스러운 것처럼 보입니다.


내가 아는 대부분의 순수 주의자들은 당신과 동의하지 않을 것입니다.

0

약속을 처리하는 것은 "if"조건 과 거의 비슷 합니다. 인증이 실패한 경우 "해결"또는 "거부"여부는 귀하에게 달려 있습니다.


1
약속은 비동기 try..catch하지 if.
zzzzBov

@zzzBox는 그 논리에 따라 Promise를 비동기식으로 사용하고 try...catch완료하고 결과를 얻을 수 있다면받은 값에 관계없이 해결해야한다고 말하고 그렇지 않으면 거부해야합니까?

@ 아무것도, 당신은 내 주장을 잘못 해석했습니다. try { if (!doSomething()) throw whatever; doSomethingElse() } catch { ... }완벽하게 괜찮지 만 a를 Promise나타내는 구성은 try..catch부분이 아니라 if부분입니다.
zzzzBov

@zzzzBov 나는 모든 공정성에서 그것을 얻었다 :) 나는 비유를 좋아한다. 그러나 내 논리는 단순히 doSomething()실패하면 던져 질 것입니다. 그러나 그렇지 않으면 필요한 값을 포함 할 수 있습니다 ( if위 의 아이디어는 여기에서 귀하의 아이디어의 일부가 아니라 약간 혼동됩니다 ). 던질 이유가있는 경우 (비유로) 거부해야하므로 테스트가 실패한 경우. 테스트에 성공하면 값이 양수인지 여부에 관계없이 항상 해결해야합니까?

@ 어딘가에, 나는 의견이 내 생각을 표현하기에 충분하지 않기 때문에 대답을 작성하기로 결정했습니다 (이것은 충분히 오래 열려 있다고 가정합니다).
zzzzBov
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.