약속 체인은 오류 처리에 탁월합니다. Promise가 거부되면 컨트롤은 가장 가까운 거부 핸들러로 이동합니다. 실제로는 매우 편리합니다.
예를 들어 아래 코드에서 fetch
URL이 잘못되었으며(해당 사이트 없음) .catch
가 오류를 처리합니다.
fetch('https://no-such-server.blabla') // 거부 .then(응답 => response.json()) .catch(err => Alert(err)) // TypeError: 가져오지 못했습니다(텍스트는 다를 수 있음).
보시다시피 .catch
가 즉시 실행될 필요는 없습니다. 이는 하나 또는 여러 개의 .then
뒤에 나타날 수 있습니다.
또는 사이트에 모든 것이 문제가 없지만 응답이 유효한 JSON이 아닐 수도 있습니다. 모든 오류를 잡는 가장 쉬운 방법은 체인 끝에 .catch
추가하는 것입니다.
가져오기('https://javascript.info/article/promise-chaining/user.json') .then(응답 => response.json()) .then(사용자 => 가져오기(`https://api.github.com/users/${user.name}`)) .then(응답 => response.json()) .then(githubUser => new Promise((해결, 거부) => { img = document.createElement('img'); img.src = githubUser.avatar_url; img.className = "약속-아바타-예제"; document.body.append(img); setTimeout(() => { img.remove(); 해결(githubUser); }, 3000); })) .catch(오류 => 경고(error.message));
일반적으로 이러한 .catch
전혀 트리거되지 않습니다. 그러나 위의 Promise 중 하나라도 거부되면(네트워크 문제 또는 유효하지 않은 json 등) 이를 포착합니다.
Promise 실행기와 Promise 핸들러의 코드 주위에는 "보이지 않는 try..catch
"가 있습니다. 예외가 발생하면 이를 포착하여 거부로 처리합니다.
예를 들어 다음 코드는 다음과 같습니다.
new Promise((해결, 거부) => { throw new Error("앗!"); }).catch(경고); // 오류: 이런!
…이와 정확히 동일하게 작동합니다.
new Promise((해결, 거부) => { 거절(new Error("앗!")); }).catch(경고); // 오류: 이런!
실행기 주변의 "보이지 않는 try..catch
"는 자동으로 오류를 포착하고 이를 거부된 Promise로 전환합니다.
이는 실행기 함수뿐만 아니라 해당 핸들러에서도 발생합니다. .then
핸들러 내부에 throw
경우 이는 약속이 거부되었음을 의미하므로 컨트롤은 가장 가까운 오류 핸들러로 점프합니다.
예는 다음과 같습니다.
new Promise((해결, 거부) => { 해결("확인"); }).then((결과) => { throw new Error("앗!"); // 약속을 거부합니다. }).catch(경고); // 오류: 이런!
이는 throw
문으로 인해 발생한 오류뿐만 아니라 모든 오류에 대해 발생합니다. 예를 들어 프로그래밍 오류는 다음과 같습니다.
new Promise((해결, 거부) => { 해결("확인"); }).then((결과) => { 블라블라(); // 그런 기능은 없습니다 }).catch(경고); // 참조 오류: blabla가 정의되지 않았습니다.
최종 .catch
명시적인 거부뿐만 아니라 위 핸들러의 우발적인 오류도 포착합니다.
이미 알고 있듯이 체인 끝에 있는 .catch
try..catch
와 유사합니다. 원하는 만큼 .then
핸들러를 가질 수 있으며, 마지막에 단일 .catch
사용하여 모든 핸들러의 오류를 처리할 수 있습니다.
일반적인 try..catch
에서는 오류를 분석하고 처리할 수 없는 경우 오류를 다시 발생시킬 수 있습니다. 약속에도 같은 일이 가능합니다.
.catch
내부에 throw
컨트롤은 다음으로 가장 가까운 오류 처리기로 이동합니다. 그리고 오류를 처리하고 정상적으로 완료되면 다음으로 가장 가까운 성공적인 .then
핸들러로 계속됩니다.
아래 예에서는 .catch
가 오류를 성공적으로 처리합니다.
// 실행: catch -> then new Promise((해결, 거부) => { throw new Error("앗!"); }).catch(함수(오류) { Alert("오류가 처리되었습니다. 정상적으로 계속 진행합니다."); }).then(() => Alert("다음 성공적인 핸들러가 실행됩니다."));
여기서 .catch
블록은 정상적으로 완료됩니다. 따라서 다음으로 성공한 .then
핸들러가 호출됩니다.
아래 예에서는 .catch
의 다른 상황을 볼 수 있습니다. 핸들러 (*)
는 오류를 포착하고 이를 처리할 수 없으므로(예: URIError
처리하는 방법만 알고 있음) 오류를 다시 발생시킵니다.
// 실행: catch -> catch new Promise((해결, 거부) => { throw new Error("앗!"); }).catch(함수(오류) { // (*) if (URIError의 오류 인스턴스) { //처리해 } 또 다른 { Alert("해당 오류를 처리할 수 없습니다."); 오류 발생; // 이 오류나 다른 오류를 던지면 다음 오류로 이동합니다. } }).then(함수() { /* 여기서는 실행되지 않습니다 */ }).catch(오류 => { // (**) Alert(`알 수 없는 오류가 발생했습니다: ${error}`); // 아무것도 반환하지 않습니다 => 실행은 일반적인 방식으로 진행됩니다. });
실행은 체인 아래의 첫 번째 .catch
(*)
에서 다음 항목 (**)
으로 점프합니다.
오류가 처리되지 않으면 어떻게 되나요? 예를 들어, 다음과 같이 체인 끝에 .catch
추가하는 것을 잊었습니다.
새로운 약속(함수() { noSuchFunction(); // 여기에 오류가 있습니다(해당 기능 없음). }) .then(() => { // 성공적인 약속 처리기, 하나 이상 }); // 끝에 .catch가 없습니다!
오류가 발생하면 Promise가 거부되고 실행은 가장 가까운 거부 핸들러로 점프해야 합니다. 그러나 아무것도 없습니다. 따라서 오류가 "고착"됩니다. 이를 처리할 코드가 없습니다.
실제로 코드에서 처리되지 않은 일반적인 오류와 마찬가지로 이는 심각한 문제가 발생했음을 의미합니다.
일반 오류가 발생하고 try..catch
에 의해 포착되지 않으면 어떻게 되나요? 스크립트는 콘솔에 메시지와 함께 종료됩니다. 처리되지 않은 약속 거부에서도 비슷한 일이 발생합니다.
JavaScript 엔진은 이러한 거부를 추적하고 이 경우 전역 오류를 생성합니다. 위의 예제를 실행하면 콘솔에서 확인할 수 있습니다.
브라우저에서는 unhandledrejection
이벤트를 사용하여 이러한 오류를 포착할 수 있습니다.
window.addEventListener('unhandledrejection', function(event) { // 이벤트 객체에는 두 가지 특별한 속성이 있습니다. 경고(event.promise); // [object Promise] - 오류를 생성한 Promise 경고(이벤트.이유); // 오류: 이런! - 처리되지 않은 오류 객체 }); 새로운 약속(함수() { throw new Error("앗!"); }); // 오류를 처리할 캐치가 없습니다.
이벤트는 HTML 표준의 일부입니다.
오류가 발생하고 .catch
가 없으면 unhandledrejection
핸들러가 트리거되고 오류에 대한 정보가 포함된 event
객체를 가져오므로 무언가를 할 수 있습니다.
일반적으로 이러한 오류는 복구할 수 없으므로 가장 좋은 방법은 사용자에게 문제에 대해 알리고 해당 사건을 서버에 보고하는 것입니다.
Node.js와 같은 비브라우저 환경에서는 처리되지 않은 오류를 추적하는 다른 방법이 있습니다.
.catch
모든 종류의 프라미스에서 발생하는 오류를 처리합니다( reject()
호출이든 핸들러에서 발생한 오류이든).
.then
두 번째 인수(오류 처리기)가 주어지면 동일한 방식으로 오류를 포착합니다.
오류를 처리하고 싶은 위치에 정확히 .catch
배치하고 처리 방법을 알아야 합니다. 핸들러는 오류를 분석하고(사용자 정의 오류 클래스 도움말) 알 수 없는 오류(프로그래밍 실수일 수 있음)를 다시 발생시켜야 합니다.
오류를 복구할 수 있는 방법이 없다면 .catch
전혀 사용하지 않아도 됩니다.
어떤 경우든 우리는 처리되지 않은 오류를 추적하고 이에 대해 사용자(아마도 우리 서버)에게 알리기 위해 (브라우저용, 다른 환경용 유사) unhandledrejection
이벤트 핸들러를 보유해야 합니다. 그러면 우리 앱은 결코 "그냥 죽지" 않습니다.
어떻게 생각하나요? .catch
가 실행되나요? 당신의 대답을 설명하십시오.
new Promise(함수(해결, 거부) { setTimeout(() => { throw new Error("앗!"); }, 1000); }).catch(경고);
대답은 다음과 같습니다. 아니요, 그렇지 않습니다 .
new Promise(함수(해결, 거부) { setTimeout(() => { throw new Error("앗!"); }, 1000); }).catch(경고);
이 장에서 말했듯이 함수 코드 주위에는 "암시적 try..catch
"가 있습니다. 따라서 모든 동기 오류가 처리됩니다.
하지만 여기서는 실행 프로그램이 실행되는 동안이 아니라 나중에 오류가 발생합니다. 그래서 약속은 그것을 처리할 수 없습니다.