function fetchUser() {
//do network request in 10 secs....
return 'ryan'
}
const user = fetchUser();
console.log(user);
이처럼 한 줄이 끝나야 다음 줄로 넘어가는 것은 동기적수행으로, fetchUser가 호출이 되면 선언된 곳으로 가서 함수의 코드블럭을 실행한다. 실행되면서 10초가 걸리니까, 끝날 때까지 10초동안 머물러 있다가 성공적으로 네트워크 데이터를 받아오면 값을 받아오면서 ryan 값을 return한다. 이 값이 user에 할당되고, 다음 코드인 console.log에 따라 ryan이 출력된다.
여기서 우리가 비동기적으로 처리를 하지않으면, 10초가 걸리기 때문에 만약 뒤에서 웹페이지의 UI를 수행하는 기능이 있다면, 끝나는 동안 웹페이지에 표시되지 않기 때문에 사용자는 10초동안 비어있는 페이지를 보게 될 것이다. 그래서 비동기적으로 처리를 해줘야한다!
❗️그래서 Promise로 만들어주면 된다.
function fetchUser() {
return Promise((resolve, reject) => {
resolve('ryan');
});
//do network request in 10 secs....
}
const user = fetchUser();
user.then(console.log);
console.log(user);
언제 user의 데이터를 받아올지는 모르겠지만, Promise라는 Object를 가지고 있으면 여기에 then이라는 콜백함수만 등록해 놓으면, User의 데이터가 준비되는대로, 콜백함수를 나중에 불러줄게 약속하는 것이다.
결과적으로, fetchUser는 Promise를 return하기 때문에, 우리가 then이라는 콜백함수를 이용해서 user가 들어오면 console.log를 출력하게 된다.
Promise안에는 resolve와 reject를 받는 executor라는 콜백함수를 만들게 되었었다.
그래서 코드 블럭안에 있는 것들이 비동기적으로 수행이 되었었다.
resolve와 reject를 호출하지 않고, 바로 return을 해주면 pending 상태가 되어 있는 것을 볼 수 있다. 그래서 꼭 Promise안에는 resolve와 reject를 통해서 완료를 해주어야 한다. 바꾸게 되면, promise 상태가 fulfilled로 바뀌면서 결과값도 ryan으로 바뀌게 된다.
이렇게 Promise를 이용하지 않고서도 간편하게 비동기를 작성할 수 있는 방법이 있는데, 함수앞에 async라는 keyword를 붙여주면 된다. 자동적으로 함수 안에 있는 코드 블럭들이 프로미스로 변환되어진다.
Promise를 조금 더 간결하고 간편하고, 동기적으로 실행되는 것처럼 보이게 해주는 기능!
여러가지 promise를 then을 통해 chaining을 계속 하게 되면 코드가 난잡해 질 수도 있는데, 위에 간편한 API로 async와 await을 사용하면, 동기식으로 코드를 작성하는 것처럼 간편하게 작성할 수 있도록 도와준다.
기존에 존재하는 promise위에 조금 더 간편한 API를 제공하는 것!
이렇게 기존하는 것을 감싸서 조금 더 간편하게 쓸 수 있는 API를 제공하는 것을 syntactic sugar라고 한다.
async function fetchUser() {
//do network request in 10 secs....
return 'ryan'
}
const user = fetchUser();
user.then(console.log);
console.log(user);
await은 async가 붙은 함수안에서만 쓸 수 있는데, delay라는 함수는 정해진 ms가 지나면 resolve를 호출하는 Promise를 return한다.
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getChestnut() {
await delay(2000);
return '🌰';
}
await을 넣는다면, 2초라는 delay가 끝날 때까지 기다려준다. 2초뒤에 밤을 return한다. 땅콩 또한 2초 뒤에 return한다.
얘네들을 굳이 Promise를 쓰는 함수로 만들어보자면,
function getChestnut() {
return delay(2000)
.then(()=>'🌰');
근데 이렇게 chaining하는 것보다, 동기적인 코드를 것처럼! 만들게 되면 딜레이가 끝날 때까지 기다려! 기다렸다가 밤을 리턴한다라고 쉽게 이해할 수 있다
into the deep
밤과 땅콩 한번에 따오는 함수를 만들어볼건데, 기존의 Promise Chaining이용하면,
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getChestnut() {
await delay(2000);
return '🌰';
}
async function getPeanut() {
await delay(2000);
return '🥜';
}
function pickNuts() {
return getChestnut()
.then(chestnut => {
return getPeanut()
.then(peanut => `${chestnut} + ${peanut}`);
});
}
pickNuts()
.then(console.log);
//🌰 + 🥜
하지만 이렇게 하면 콜백지옥과 같은 문제점이 생길 수 있다. 우리는 이것을 async라는 키워드를 이용하여 간단하게 만들 수 있다. 밤과 땅콩을 다 받아와서 return을 해준다
async function pickNuts() {
const chestnut = await getChestnut();
const peanut = await getPeanut();
return `${chestnut} + ${peanut}`;
}
이렇게 async와 await라는 keyword를 이용하여 우리가 동기적으로 코드를 작성하듯이, 자연스럽게 코드를 작성하는 것처럼 쓰니 간편하게 처리할 수 있다!
async function pickNuts() {
const chestnutPromise = getChestnut();
const peanutPromise = getPeanut();
const chestnut = await getChestnut();
const peanut = await getPeanut();
return `${chestnut} + ${peanut}`;
}
pickNuts()
.then(console.log);
하지만, 밤을 받아오는데 2초가 소요, 그 다음에 땅콩을 받아오는 걸 호출하는데 2초가 걸린다. 밤과 땅콩은 전혀 연관이 없기 때문에 기다릴 필요가 없으며 이렇게 호출하는 것은 비효율적이다.
Promise를 만들면, Promise가 바로 실행되기 때문에 만들어줌으로써 해결할 수 있다
만들자마자 안의 코드가 실행되기에, 병렬적으로 동시에 한번에 기다렸다가 2초안에 나타나게 할 수 있다.
이렇게 동시다발적으로 병렬적으로 수행이 가능한 경우에는, 저렇게 코드를 작성하지 않아도 된다!
Promise에 있는 all APIs를 쓰게 되면, 모든 Promise들이 병렬적으로 받을 때까지 모아준다. 배열형태로 getChestnut, getPeanut 전달하게 되면 다 받아지면, 그러면, 다 받아진 배열이 전달이된다. nuts에 배열이 받아진다.
function pickAllNuts() {
return Promise.all([getChestnut(), getPeanut()])
.then(nuts =>
nuts.join('+')
);
}
pickAllNuts().then(console.log);
//
🌰+🥜
만약 어떤 것이든 상관없고 먼저 따지는 첫번째 견과류만 따고 싶다, 둘 중에 만약 밤을 따는데 2초, 땅콩을 따는데 1초가 걸린다하면 race라는 API를 이용한다면, 배열에 전달된 함수중에 가장 먼저 리턴된 것만 출력할 수 있다.
function pickOnlyOne() {
return Promise.race([getChestnut(), getPeanut()]);
}
pickOnlyOne()
.then(console.log)
//
🥜