1. 프라미스 체이닝
* 체이닝
- 콜백에서 언급했듯이 비동기 작업을 순차적으로 처리해야할 상황이 있다. 이 때 프라미스 체이닝을 사용하며 효율적으로 이를 다룰 수 있다.
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000);
}).then(function(result) {
console.log(result); // 1
return result * 2;
}).then(function(result) {
console.log(result); // 2
return result * 2;
}).then(function(result) {
console.log(result); // 4
return result * 2;
});
- setTimeout으로 1초후에 프라미스가 이행되어 result로 1이 전달된다. 그리고 then이 순차적으로 호출되며 반환 값을 result로 다음으로 넘긴다.
- 위와 같은 체이닝이 가능한 이유는 promise.then을 호출하면 프라미스가 반환되기 때문이다. 프라미스가 반환되므로 result가 전달되고 .then을 또 호출할 수 있는 것이다.
* 프라미스 반환
- then(handler)에서 사용된 핸들러가 프라미스를 생성하거나 반환할 수도 있다. 이 때에는 이어지는 핸들러가 프라미스가 처리될 때까지 기다리고, 처리되면 그 결과를 받는다.
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000);
}).then(function(result) {
console.log(result); // 1
return new Promise((resolve, reject) => {
setTimeout(() => resolve(result * 2), 1000);
});
}).then(function(result) {
console.log(result); // 2
return new Promise((resolve, reject) => {
setTimeout(() => resolve(result * 2), 1000);
});
}).then(function(result) {
console.log(result); // 4
});
- 위 코드는 1초 간격으로 1, 2, 4가 출력된다.
- 예시로 순서대로 스크립트를 불러와보자. 스크립트를 불러오는 함수인 loadScript는 콜백 장을 참고하자.
loadScript("./harry/one.js")
.then(function(script) {
return loadScript("./harry/two.js");
})
.then(function(script) {
return loadScript("./harry/three.js");
})
.then(function(script) {
one();
two();
three();
});
- 화살표함수로 줄이면 다음과 같다.
loadScript("./harry/one.js")
.then(script => loadScript("./harry/two.js"))
.then(script => loadScript("./harry/three.js"))
.then(script => {
one();
two();
three();
});
- loadScript를 호출할 때마다 프라미스가 반환되고 다음 .then은 그 후에 실행된다.
- 위와 같은 방식으로 콜백에서 만났던 문제를 해결할 수 있다.
2. fetch와 체이닝
* fetch와 체이닝
- 프론트 단에서 네트워크 요청을 하는 경우를 생각해보자. 서버에 요청을 할때에 비동기작업을 처리할 일이 많다.
- 사용자의 프로필 이미지를 요청하고 출력하는 예시를 보자.
fetch('https://api.harry.com/users/harry')
.then(response => response.json())
.then(user => {
let img = document.createElement('img');
img.src = user.profile_img;
img.className = "profile-image";
document.body.append(img);
setTimeout(() => img.remove(), 3000);
});
- fetch(url)을 실행하면 url에 네트워크 요청을 보내고 프라미스를 반환한다. 응답을 받으면 프라미스는 response 객체와 함께 이행된다.
- response.text() 또는 response.json()은 서버에서 받은 데이터를 텍스트나 JSON으로 result 값으로 이행된 프라미스를 반환한다.
- 위는 서버에 harry라는 유저의 정보를 요청하고 response.json()으로 JSON으로 파싱한다.
- 그리고 이미지를 문서에 추가하고 3초뒤에 지우도록 하였다.
- 하지만 위 코드에는 한 가지 문제가 있다. 만약 이미지가 사라진 이후에 다른 작업을 하고싶다면 할 수 있는 방법이 없다.
- 체인을 확장하려면, 이미지가 사라질 때 이행 프라미스를 반환해야한다.
fetch('https://api.harry.com/users/harry')
.then(response => response.json())
.then(user => new Promise(function(resolve, reject) {
let img = document.createElement('img');
img.src = user.profile_img;
img.className = "profile-image";
document.body.append(img);
setTimeout(() => {
img.remove()
resolve(user);
}, 3000);
}))
.then(user => console.log('Finish!'))
- 위와 같이 setTimeout안에 resolve를 넣어서 처리가 되면 new Promise가 반환되도록 하였다. 비동기 동작은 항상 프라미스를 반환하도록 하는 것이 좋다.
- 위를 함수로 정리하여 빼면 다음과 같다.
function showProfile(user) {
return new Promise(function(resolve, reject) {
let img = document.createElement('img');
img.src = user.profile_img;
img.className = "profile-image";
document.body.append(img);
setTimeout(() => {
img.remove()
resolve(user);
}, 3000);
})
}
fetch('https://api.harry.com/users/harry')
.then(response => response.json())
.then(showProfile)
.then(user => console.log('Finish!'))
3. 에러 핸들링
* 숨겨진 try catch
- 프라미스 체인은 프라미스가 거부되면 제일 가까운 rejection핸들러로 넘어간다.
- 위에서 사용한 예시에서 에러가 발생한다고 가정하자.
fetch('https://api.harry.com/users/harry')
.then(response => response.json())
.then(showProfile)
.then(user => console.log('Finish!'))
.catch(error => console.log(error));
- 체인 끝에 catch만 붙이더라도 프라미스 중 하나라도 거부되면 곧바로 catch가 에러를 잡는다.
- 프라미스 생성자와 핸들러 주위에는 숨겨진 try catch가 있다. throw를 사용해서 에러를 던지지 않고 아래처럼 reject로도 에러를 만들 수 있다.
new Promise((resolve, reject) => {
reject(new Error("에러 발생!"));
}).catch(alert); // Error: 에러 발생!
- throw나 reject가 아닌 모든 종류의 에러가 암시적 try catch에서 처리된다.
* 다시 던지기
- 일반적인 try catch에서 우리는 처리할 수 없는 에러라 판단되면 다시 던졌다. 프라미스에서도 유사한 작업을 구현해보자.
new Promise((resolve, reject) => {
throw new Error("에러 발생!");
}).catch(function(error) {
if (error instanceof URIError) {
// 에러 처리
} else {
alert("처리할 수 없는 에러");
throw error; // 에러 다시 던지기
}
}).then(function() {
// 에러가 처리되면 실행
}).catch(error => {
alert(`알 수 없는 에러가 발생함: ${error}`);
});
- 위에서 먼저 에러를 발생시켜 catch로 넘어갔다. 여기서 if로 처리 가능한 에러일 경우 처리하도록 하였고, else로 처리가 불가능하면 다시 던지도록 하였다.
- 만약 에러가 처리된다면 then으로 넘어가게된다. 그러나 에러가 처리되지 못하면 다음 catch로 넘어간다.
참고
모던 자바스크립트 튜토리얼
'Language > JavaScript' 카테고리의 다른 글
<ES6> ES2015(ES6) Features (2) (0) | 2021.08.01 |
---|---|
<ES6> ES2015(ES6) Features (1) (0) | 2021.08.01 |
<자바스크립트> 프라미스 (0) | 2021.04.21 |
<자바스크립트> 콜백 (0) | 2021.04.19 |
<자바스크립트> try catch & 에러 (0) | 2021.04.16 |