Promise와 Asynchronous

이효범·2022년 4월 26일
0

ES6 - OOP & FRP

목록 보기
13/15
post-thumbnail

Promise는 Asynchronous Programming의 가장 기초가 되는 지식이다.

Promise

Promise는 어떠한 동작을 수행하고 난 뒤에 무엇인가를 요청을 하게 되며 이를 콜백이라고 한다.

또한, 동작을 수행하고 있는 상태를 status라고 얘기를 하는데 그 상태는 셋 중에 하나이다.
첫째는 Unresolved, 아직까지 일이 끝나지 않았다는 것이고
둘째로는 Resolved, 일이 끝났다는 것이고
마지막으로는 Rejected, 거부되었다는 것이다.

주로 Promise는 웹 서버로부터 어떠한 데이터를 받아올 때 사용된다.
어떠한 데이터를 받아오는 과정은 Unresolved, 이미 다 받아와서 완료가 되었다면 Resolved, 만약 웹 서버가 줄 수 없다고 거부할 때는 Rejected 되는 것이다.

Resolved 상황에서는 then 이라는 콜백이 호출되고,
Rejected 상황에서는 catch 라고 하는 콜백이 호출되게 한다.
이 둘은 Promise 클래스에 정의되어있는 일종의 메소드들이다.

콜백이라고 하는 것은 어떠한 상황이 닥쳤을 때 호출되는 형태의 함수를 의미한다.
즉, Resolved 상황이 되어야지 then 이라는 콜백이 호출되고
Rejected 라는 상황이 되어야만 catch 라는 콜백이 호출되는 형태이다.

따라서 콜백은 어떠한 상황을 기다리고 있다가 어떠한 진행이 완료되어 결과가 나온다면 그때 호출되게 된다.

다음 코드를 살펴보자.

// Asynchronous
const promise = new Promise();
console.log(promise); 
// Promise resolver undefined is not a function

위의 promise 를 콘솔창에 찍어보면, resolver 가 undefined 라는 문구가 나오게 된다.
그럼 resolver 는 무엇인가?

Promise를 만들 때는 반드시 무엇인가를 인자로 전달해주어야하는데,
그 인자가 바로 resolve 이다.

다음과 같이 function을 전달해주면 된다.

const promise = new Promise(() => {});
console.log(promise); 
// Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}

이제 보니까 Promise가 현재 pending 상태라고 되있고,
pending 은 위에서 살펴본 Unresolved 상태를 의미한다.

이제 argument를 던져주도록 하자.

const promise = new Promise((resolve, reject) => {
	resolve();
});
console.log(promise); 
// Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: undefined}

좀 전에 pending 상태에서 이제는 resolved 상태로 바뀌었다. 만약 여기서 resolve()reject() 로 변경하면은 PromiseStatus 는 rejected 상태로 바뀌게 된다.

이제 실제로 사용되는 방식을 살펴보자. Promise 뒤에 따라붙을 수 있는 then과 catch를 사용한다.

const promise = new Promise((resolve, reject) => {
	resolve();
});
console.log(promise); 

promise
  .then(() => { console.log('first finished'); })
  .then(() => { console.log('second finished'); })
  .then(() => { console.log('third finished'); })
  .catch(() => { console.log('에러 발생'); })
// first finished
// second finished
// third finished

then 안에 들어가는 것이 콜백 함수이다.
또한 왜 이게 콜백이냐니까 이 콜백이 호출되는 시점이 바로 Promise 내부의 resolve 함수가 완료되었을 때 실행되기 때문이다.
또한 이러한 콜백 함수는 위의 코드처럼 연달아서 실행시킬 수 있다.
우선의 콜백이 또다시 완료된다면, 그 뒤의 콜백이 호출되어 실행되는 식이다.

또한 에러도 마찬가지이다. catch를 에러가 발생한 경우에 사용하게 되는데, 마찬가지로 catch안에 들어가게 되는 것도 콜백이다.
하지만 위의 코드에서는 PromiseStatus 가 resolve 상태이기 때문에, catch 콜백은 실행되지 않는다.

곧 resolve 가 되지 않았을때, 즉 rejected 상태일 때 catch 콜백 함수가 호출되어 실행되게 된다. catch 콜백은 만약 Promise 내부에서 rejected 상태가 일어난다면 다른 콜백 함수들은 모두 건너뛰게되고 바로 호출되게된다.


Asynchronous

Promise는 Asynchronous Programming에서 대표적으로 사용되는 개념인데,
Asynchronous 라고 하는 것은 컴퓨터가 코드를 읽고 해석을 하고 계산을 하는 시점과,
실제 그것이 실행되는 사이에 시간적 간격이 존재하는 것을 의미한다.
대표적으로 setTimeout 같은 경우가 존재한다.

const promise = new Promise((resolve, reject) => {
	setTimeout(() => { resolve() }, 3000);
});
console.log(promise); 

promise
  .then(() => { console.log('first finished'); })
  .then(() => { console.log('second finished'); })
  .then(() => { console.log('third finished'); })
  .catch(() => { console.log('에러 발생'); })
// first finished
// second finished
// third finished

setTimeout은 argument가 두개이다. 첫째는 함수를 하나 받고
둘째로는 이 함수가 실행되는 시점이다.
위의 코드는 3초 후에 앞의 함수를 실행해라라는 의미를 가진다.

따라서 컴퓨터가 첫 번째 인자인 함수를 읽는 시점과
이 함수가 실제로 실행하는 시점 사이에는 3초라는 시간 간격이 존재하는 것이다.
이러한 방식을 Asynchronous 라고 한다.

실제 구체적으로 쓰이는 경우는 request 와 같은 것들을 사용하게 되는 때이다.

const promise = new Promise((resolve, reject) => {
  let request = new XMLHttpRequest(); // 웹에서 어떠한 데이터를 가져오기 위한 요청 코드
  
  request.onload = () => {
   resolve(); 
  }
  // 서버에서 데이터가 보내져서 내 컴퓨터에 도착하게 될 시실행되는 코드 
  // 즉, 데이터를 요청한 시점과 실제 데이터가 도착한 시점 그 사이의 간격이 존재한다. => Asynchronous
});
console.log(promise); 

promise
  .then(() => { console.log('first finished'); })
  .then(() => { console.log('second finished'); })
  .then(() => { console.log('third finished'); })
  .catch(() => { console.log('에러 발생'); })
// first finished
// second finished
// third finished

Promise와 Asynchronous 예시

첫 번째 예시

실제로 사용되는 예를 하나 보도록 하자.
fetch 는 Promise를 이용하는 대표적인 방식이다.

url = "https://jsonplaceholder.typicode.com/posts/"

fetch(url)   // 컴퓨터가 fetch함수를 읽는 시점과 실제 url로부터 response를 받는 시점과의 시간적 차이점을 then으로써 연결하고 있다.
	.then(response => response.json())
	.then(data => console.log(data))
	.catch(error => console.log('Failed due to weak internet connection', error))

두 번째 예시

두 번째 예시도 보도록 하자.
밑의 예시는 우선 Promise 를 사용하여 코드를 작성해보고
그 후 async await 를 사용하여 리팩토링을 해보도록 한다.

Promise

const button = document.querySelector('button');
const div = document.querySelector('div');

const setText = (text) => {
 div.textContent = text; 
}

const checkAuth = () => {
 return new Promise((resolve, reject) => {
  setText('Checking Auth...')
  setTimeout(() => {
   resolve(true); 
  }, 3000);
 });
};

const fetchUser = () => {
  return new Promise((resolve, reject) => {
    setText('Fetching User...')
    setTimeout(() => {
     resolve({ name: "Superman" }); 
    }, 3000);
  });
};

button.addEventListener("click", () => {
 checkAuth()
  	.then(
   		isAuth => {
          if (isAuth) {
           return fetchUser() 
          }
        }
    )
    .then(
    	user => {
          setText(user.name)
        }  
    )
});

async await

const button = document.querySelector('button');
const div = document.querySelector('div');

const setText = (text) => {
 div.textContent = text; 
}

const checkAuth = () => {
 return new Promise((resolve, reject) => {
  setText('Checking Auth...')
  setTimeout(() => {
   resolve(true); 
  }, 3000);
 });
};

const fetchUser = () => {
  return new Promise((resolve, reject) => {
    setText('Fetching User...')
    setTimeout(() => {
     resolve({ name: "Superman" }); 
    }, 3000);
  });
};

button.addEventListener("click", async () => {
  const isAuth = await checkAuth()
  let user = null;
  if (isAuth) {
   user = await fetchUser() 
  }
  setText(user.name)
});

profile
I'm on Wave, I'm on the Vibe.

0개의 댓글