JS 비동기 (PurpleCode Study)
동기와 비동기
-
동기
: 작업이 끝날 때까지 기다리는 동안 중지 상태가 디고, 해당 작업이 끝나야 비로서 그 다음 예정된 작업을 할 수 있습니다. -
비동기 처리
: 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고, 다음 코드를 먼저 실행하는 자바스크립트의 특성을 의미합니다.
흐름이 멈추지 않기 때문에 동시에 여러가지 작업을 처리할 수 있고, 기다리는 과정에서 다른 함수도 호출할 수 있습니다.
비동기 처리의 사례들
- 제이쿼리의
ajax
- setTimeout()
콜백 함수로 비동기 처리 방식의 문제점 해결하기
콜백 함수
를 사용하여 특정 로직이 끝났을 때 원하는 동작을 실행할 수 있습니다.
콜백 함수
의 문제점: 콜백 지옥
- 가독성이 매우 떨어지고, 로직을 변경하기도 힘듭니다.
콜백 지옥을 해결하는 방법:
Promise
프로미스
는 자바스크립트 비동기 처리에 사용되는 객체입니다.
const getData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ name: "김지원"});
}, [5000]);
});
};
getData().then(userData => console.log(userData));
Promise의 3가지 상태
new Promise()
로 프로미스를 생성하고 종료될 때까지 3가지의 상태를 갖습니다.
- Pending(대기): 비동기 처리 로직이 아직 완료되지 않은 상태
- Fulfilled(이행): 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
- Rejected(실패): 비동기 처리가 실패하거나 오류가 발생한 상태
new Promise()
메서드를 호출하면 대기상태가 됩니다.
new Promise();
- 메서드를 호출할 때 콜백 함수를 선언할 수 있고, 콜백함수의 인자는
resolve
,reject
입니다.
new Promise((resolve, reject) => {} );
-
여기서 콜백함수의 인자
resolve
를 실행하면 이행상태가 됩니다.resolve(value): 일이 성공적으로 끝난 경우, 그 결과를 나타내는 value와 함께 호출
new Promise((resolve, reject) => {
resolve();
});
- 이행 상태가 되면
then()
을 이용하여 처리 결과 값을 받을 수 있습니다.
const getId = () => {
const userId = { id:"annie1004619", name="김지원" };
return new Promise((resolve, reject) => {
resolve(userId);
});
};
getId().then(userId => console.log(userId));
-
new Promise()
로 프로미스 객체를 생성하고reject
를 호출하면 실패상태가 됩니다.reject(error): 에러 발생 시 에러 객체를 나타내는 error와 함께 호출
new Promise((resolve, reject) => {
reject();
});
- 그리고 실패 상태가 될 경우
catch()
로 받을 수 있습니다.
const getId = () => {
const userId = { id: "annie1004619", name: "김지원" };
return new Promise((resolve, reject) =>{
reject(new Error("request is failed"));
});
};
getId()
.then(userId => console.log(userId))
.catch(err => console.log(err));
종합한 간단한 프로미스 코드
const getId = () =>{
const userId = { id: "annie1004619", name: "김지원" };
return new Promise((resolve, reject)=>{
if(userId){
resolve(userId);
}
reject(new Error("Request is failed"));
});
};
getId()
.then(userId => console.log(userId));
.catch(err => console.log(err));
여러 개의 프로미스를 연결하기 (Promise Chaining)
프로미스의 또 다른 특징은 여러 개의 프로미스를 연결하여 사용할 수 있다는 점 입니다.
new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(1);
}, 2000);
})
.then(function(result) {
console.log(result); // 1
return result + 10;
})
.then(function(result) {
console.log(result); // 11
return result + 20;
})
.then(function(result) {
console.log(result); // 31
});
- 2초 후 최초 프라미스가 실행됩니다.
- 이후
.then
핸들러가 호출됩니다. - 2에서 반환한 값은 다음
.then
핸들러에 전달됩니다. - 이런 과정이 계속 이어집니다.
문제 1) 프라미스: then vs catch
두 코드 조각이 동일하게 동작할까요?
promise.then(f1).catch(f2);
promise.then(f1, f2);
답은 같지 않다!
promise
.then(f1)
.catch(f2);
f1
에서 에러가 밸생하면 위 코드에서는 .catch
에서 에러가 처리됩니다.
promise
.then(f1, f2);
하지만 위 코드에서는f1
에서 발생한 에러를 처리하지 못합니다.
문제 2) 프로미스로 지연 만들기
내장 함수setTimeout
은 콜백을 사용합니다.
프라미스를 기반으로 하는 동일 기능 함수를 만들어 보세요!
function delay(ms){
return new Promise(resolve => setTimeout(resolve, ms));
}
delay(3000).then(()=>alert('3초 후 실행'));
resolve
가 인수 없이 호출되었다. 함수 delay
는 지연 확인 용이기 때문에 반환 값이 필요 없습니다.
async & await
: 자바스크립트 비동기 처리 패턴 중에서 가장 최근에 나온 문법입니다.
기존의 비동기 처리 방식인 콜백함수와 프로미스의 단점을 보완하고 순차적으로 읽으면서 사고할 수 있는 코드를 작성할 수 있게 도와줍니다.
async & await의 기본 문법
async function 함수명() {
await 비동기_처리_메서드_명();
}
const 함수명 = async () => {
await 비동기_처리_메서드_명();
};
주의 해야할 점: 비동기 처리 메서드가 꼭 프로미스를 반환해야 await
가 의도한 대로 동작합니다.
const fetchMenu = () => {
return new Promise(function(resolve, reject) {
let Menu = ["아이스 아메리카노","카페 라떼","토피넛 라떼"];
resolve(items);
});
};
async function loadUsers() {
let data = await fetchMenu();
console.log(data); //["아이스 아메리카노","카페 라떼","토피넛 라떼"]
}
async & await 실용 예제
async & await
은 여러개의 비동기 처리 코드를 다룰 때 유용합니다.
const fetchMenuType = () =>{
return new Promise((resolve, reject) => {
resolver("milk beverage");
});
};
const fetchMenuName = () =>{
return new Promise((resolve, reject) => {
resolve("녹차 라떼");
});
};
const getData = async ()=> {
const menuType = await fetchMenuType();
if(menuType === "milk beverage"){
const menuName = await fetchMenuName();
console.log(menuName);//녹차 라떼
}
};
async & await 예외 처리
예외 처리는 try catch
로 할 수 있습니다.
const getData = async ()=> {
const menuType = await fetchMenuType();
if(menuType === "milk beverage"){
const menuName = await fetchMenuName();
console.log(menuName);//녹차 라떼
}
}catch (e) {
console.log(e);
}
};
실행하다가 발생한 네트워크 오류 뿐 아니라 간단한 타입 오류 등 일반적인 오류까지도 catch로 잡아낼 수 있습니다.
프로미스 API
Promise
클래스에는 2가지 정적 메서드가 남아있습니다.
1. Promise.all
:
여러 개의 프라미스를 동시에 실행시키고 모든 프라미스가 준비될 때까지 기다린다고 했을 때(복수의 URL에 동시에 요청을 보내고, 다운로드가 모두 완료된 후에 콘텐츠를 처리할 때 이런 상황이 발생합니다.) Promise.all
을 사용할 수 있습니다.
사용법
Promise.all([...promises]);
Promise.all
은 요소 전체가 프라미스인 배열을 받고 새로운 프라미스를 반환합니다.
배열 안 프라미스가 모두 처리되면 새로운 프라미스가 이행되는데, 배열 안 프라미스의 결괏값을 담은 배열이 새로운 프라미스의result
가 됩니다.
EX) Promise.all
은 3초 후에 처리되고 반환되는 프라미스의 result
는 배열 [1,2,3]이 됩니다.
Promise.all([
new Promise(resolve => setTimeout(() => resolve(1), [3000])),
new Promise(resolve => setTimeout(() => resolve(2), [2000])),
new Promise(resolve => setTimeout(() => resolve(3), [1000]))
]).then(console.log); // 1,2,3
배열 result
의 요소 순소는 Promise.all
에 전달되는 프라미스 순서와 상응합니다.
Promise.all
의 첫번째 프라미스는 가장 늦게 이행되더라도 처리 결과는 배열의 첫 번째 요소에 저장됩니다.
EX )GitHub 유저네임이 담긴 배열을 사용해 사용자 정보를 가져오는 예시를 살펴봅시다 (id를 기준으로 장바구니 목록을 불러올 때도 같은 로직을 사용할 수 있습니다).
let names = ['iliakan', 'remy', 'jeresig'];
let requests = names.map(name => fetch(`https://api.github.com/users/${name}`));
Promise.all(requests)
.then(responses => {
// 모든 응답이 성공적으로 이행되었습니다.
for(let response of responses) {
alert(`${response.url}: ${response.status}`); // 모든 url의 응답코드가 200입니다.
}
return responses;
})
// 응답 메시지가 담긴 배열을 response.json()로 매핑해, 내용을 읽습니다.
.then(responses => Promise.all(responses.map(r => r.json())))
// JSON 형태의 응답 메시지는 파싱 되어 배열 'users'에 저장됩니다.
.then(users => users.forEach(user => alert(user.name)));
2. Promise.allSettled
:
Promise.all
과 차이가 있는 메서드 입니다.
먼저 Promise.all
은 프라미스가 하나라도 거절되면 전체를 거절합니다. 따라서 프라미스 결과가 모두 필요할 때 같이 모 아니면 도 일 경우 유용하게 사용됩니다.
반면에 Promise.allSettled
는 모든 프라미스가 처리될 때까지 기다립니다.
반환되는 배열은 다음과 같은 요소를 가집니다.
- 응답이 성공할 경우: {status: "fulfilled", value: result}
- 에러가 발생할 경우: {status: "rejected", reason: error}
fetch를 사용해 여러 사람의 정보를 가져오고 있다고 해봅시다. 여러 요청 중 하나가 실패해도 다른 요청 결과는 여전히 있어야 합니다.
이럴 때 Promise.allSettled
를 사용할 수 있습니다.
Author And Source
이 문제에 관하여(JS 비동기 (PurpleCode Study)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@annie1004619/JS-비동기-PurpleCode-Study저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)