0%

210201_TIL(Promise_2)

오늘 배운 것

프로미스

비동기 처리로 발생한은 콜백헬과 에러처리에 대한 어려움으로 ES6에서 프로미스가 등장하였다.

콜백을 사용한 GET 비동기 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// GET 요청을 위한 비동기 함수
const get = (url, callback) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.send();

xhr.onload = () => {
if (xhr.status === 200) {
// 서버의 응답을 콜백 함수에 전달하면서 호출하여 응답에 대한 후속 처리를 한다.
callback(JSON.parse(xhr.response));
} else {
console.error(`${xhr.status} ${xhr.statusText}`);
}
};
};

const url = "https://jsonplaceholder.typicode.com";

// id가 1인 post의 userId를 취득
get(`${url}/posts/1`, ({ userId }) => {
console.log(userId); // 1
// post의 userId를 사용하여 user 정보를 취득
get(`${url}/users/${userId}`, (userInfo) => {
console.log(userInfo); // {id: 1, name: "Leanne Graham", username: "Bret",...}
});
});

프로미스와 후속처리 메서드를 사용한 GET 메서드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// GET 요청을 위한 비동기 함수
const get = (url, callback) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.send();

xhr.onload = () => {
if (xhr.status === 200) {
// 서버의 응답을 콜백 함수에 전달하면서 호출하여 응답에 대한 후속 처리를 한다.
resolve(JSON.parse(xhr.response));
} else {
reject(new Erro(xhr.status));
}
};
});
};

const url = 'https://jsonplaceholder.typicode.com';

// 후속처리 메서드 then도 결국 콜백을 사용하게 된다.
get(`${url}/posts/1`)
.then(({ userId }) => get(`${url}/users/${userId}`));
.then(console.log);

후속처리 메서드는 일의 순서 대로 작성하는 것이 가독성이 좋다.

프로미스는 프로미스 객체를 반환하기 때문에 return 값이 프로미스 객체라면 바로 반환이 가능하지만 아니라면 프로미스 객체를 생성하여 값을 설정하고 넘긴다.

위의 경우 console.log 메서드가 중간에서 후속처리를했었다면 명시적으로 반환값을 넘기지 않으면 프로미스에 undefined 값을 설정하고 반환한다.

1
2
3
4
5
6
get(`${url}/posts/1`)
.then((post) => {
console.log(post); // 1
return post;
})
.then(({ userId }) => get(`${url}/users/${userId}`));

Promise.all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const requestData1 = () =>
new Promise((resolve) => setTimeout(() => resolve(1), 3000));
const requestData2 = () =>
new Promise((resolve) => setTimeout(() => resolve(2), 2000));
const requestData3 = () =>
new Promise((resolve) => setTimeout(() => resolve(3), 1000));

// 세 개의 비동기 처리를 순차적으로 처리
const res = [];
requestData1()
.then((data) => {
res.push(data);
return requestData2();
})
.then((data) => {
res.push(data);
return requestData3();
})
.then((data) => {
res.push(data);
console.log(res); // [1, 2, 3] ⇒ 약 6초 소요
})
.catch(console.error);

위는 직렬 처리로 총 6초가 소요된다. 위와 같은 후속처리 체이닝은 위에서 작성한 GET 메서드와 같은 순차적인 처리가 필요할 때 사용하며 순차적인 처리가 필요한 경우가 아니라면 병렬 처리한다.

1
2
3
4
5
6
7
8
9
10
const requestData1 = () =>
new Promise((resolve) => setTimeout(() => resolve(1), 3000));
const requestData2 = () =>
new Promise((resolve) => setTimeout(() => resolve(2), 2000));
const requestData3 = () =>
new Promise((resolve) => setTimeout(() => resolve(3), 1000));

Promise.all([requestData1(), requestData2(), requestData3()])
.then(console.log) // [ 1, 2, 3 ] ⇒ 약 3초 소요
.catch(console.error);

위는 병렬 처리로 동시에 시작하기 때문에 총 3초가 소요된다. 병렬 처리가 훨씬 효율적이기 때문에 Promise.all을 사용해야한다.

all 메서드는 프로미스 객체로 이루어진 배열(이터러블)객체에 담아서 사용한다. 결과값으로 배열에 각 프로미스 객체의 결과값인 프로미스 객체가 순서대로 담기기 때문에 then 메서드로 받아야한다.

all 메서드에서 하나라도 rejected될 경우 모두 error 처리된다.

1
2
3
4
5
6
7
8
9
10
const githubIds = ["jeresig", "ahejlsberg", "ungmo2"];

Promise.all(
githubIds.map((id) => promiseGet(`https://api.github.com/users/${id}`))
)
// [Promise, Promise, Promise] => Promise [userInfo, userInfo, userInfo]
.then((users) => users.map((user) => user.name))
// [userInfo, userInfo, userInfo] => Promise ['John Resig', 'Anders Hejlsberg', 'Ungmo Lee']
.then(console.log)
.catch(console.error);

all 메서드는 위와 같은 GET으로 여러 값을 받을 때 사용할 수 있다.

Promise.allSettled

1
2
3
4
5
6
7
8
9
10
11
12
Promise.allSettled([
new Promise((resolve) => setTimeout(() => resolve(1), 2000)),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Error!")), 1000)
),
]).then(console.log);
/*
[
{status: "fulfilled", value: 1},
{status: "rejected", reason: Error: Error! at <anonymous>:3:54}
]
*/

모든 Settled 상태(fulfilled, rejected)를 반환한다.

Nyong’s GitHub