기타

[Javascript] 비동기 처리, Promise, async/await

코딩굼벵이 2022. 7. 13. 11:48
728x90

 

 

3장. 자바스크립트에서 비동기 처리 다루기 · GitBook

3장. 자바스크립트에서 비동기 처리 다루기 자바스크립트의 동기적 처리와 비동기 처리에 대해서 알아봅시다. 만약 작업을 동기적으로 처리한다면 작업이 끝날 때까지 기다리는 동안 중지 상태

learnjs.vlpt.us

https://learnjs.vlpt.us/async/01-promise.html

 

01. Promise · GitBook

01. Promise 프로미스는 비동기 작업을 조금 더 편하게 처리 할 수 있도록 ES6 에 도입된 기능입니다. 이전에는 비동기 작업을 처리 할 때에는 콜백 함수로 처리를 해야 했었는데요, 콜백 함수로 처

learnjs.vlpt.us

https://learnjs.vlpt.us/async/02-async-await.html

 

02. async/await · GitBook

02. async/await async/await 문법은 ES8에 해당하는 문법으로서, Promise 를 더욱 쉽게 사용 할 수 있게 해줍니다. 기본적인 사용법을 알아봅시다. function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms));

learnjs.vlpt.us


동기: 앞의 작업이 끝날 때까지 중지하고 기다림

비동기: 여러가지 작업을 멈추지 않고 처리

 

- 특정 작업이 진행되는 동안 다른 작업도 하고 싶다면 함수를 비동기 형태로 전환해야 함.
   => setTimeout 함수 사용 가능

 

setTimeout(() => { ... }, 0);

 

setTimeout: 첫번째 파라미터에 넣은 함수를 두번째 파라미터에 넣은 시간(ms) 이 흐른 후 호출하라는 의미

 

- 특정 함수가 끝난 다음 어떤 작업을 처리하고 싶을 때 (동기적, 즉 절차적으로 처리를 하고 싶을 때)

콜백 함수를 파라미터로 전달해서 특정 작업이 끝나고 호출
=> 함수 간 실행 순서를 잡아줄 수 있음

* 단, 콜백 함수로 처리하면 비동기 작업이 많아질 경우 코드가 쉽게 난잡해져서 ES6부터는 Promise 도입
흔히 아는 콜백 지옥을 생각하면 됨

function work(callback) {
    setTimeout(() => {
    	...
        console.log('특정 함수');
    	callback();
    }, 0);
}

work(() => {
	...
	console.log('어떤 작업');
})

 

 

주로 비동기적으로 처리하는 작업들

  • Ajax Web API 요청
    서버 쪽에서 데이터 받아와야할 때는, 요청을 하고 서버에서 응답할때까지 대기해야하므로 비동기적으로 처리
  • 파일 읽기
    서버 쪽에서 파일 읽어야 하는 상황에 주로 비동기적으로 처리
  • 암호화/복호화
    바로 암호화/복호화가 되지 않고 시간이 어느정도 걸릴 수 있어 비동기적으로 처리
  • 작업 예약
    단순히 어떤 작업을 몇초 후에 스케줄링 해야하는 상황에 setTimeout 사용해 비동기적으로 처리

 

Promise

function increaseAndPrint(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const value = n + 1;
      if (value === 5) {
        const error = new Error();
        error.name = 'ValueIsFiveError';
        reject(error);
        return;
      }
      console.log(value);
      resolve(value);
    }, 1000);
  });
}

increaseAndPrint(0)
  .then(increaseAndPrint)	// increaeseAndPrint(1)
  .then(increaseAndPrint)	// increaeseAndPrint(2)
  .then(increaseAndPrint)	// increaeseAndPrint(3)
  .then(increaseAndPrint)	// increaeseAndPrint(4)
  .then(increaseAndPrint)	// increaeseAndPrint(5) => 결과값 에러
  .catch(e => {
    console.error(e);
  });

 

- 비동기적으로 작업을 처리해야할 때 콜백 사용 시 코드의 깊이가 깊어지는 콜백 지옥이 생김.
   promise를 사용하면 비동기 작업의 개수가 많아져도 코드 깊이가 깊어지지 않음.

- 성공 시 resolve 호출, 실패 시 reject 호출

- resolve 호출 시 파라미터로 특정 값 넣으면 작업이 끝나고 나서도 그 값 사용 가능

- 작업 끝나고 또 다른 작업해야할 때는 promise 뒤에 `.then(...)` 붙여서 사용
   then 내부에 넣은 함수에서 또 promise를 리턴하면 연달아서 사용 가능

- reject는 사용하지 않으면 생략될 수 있음

 

promise의 불편한 점

- 에러 잡을 때 몇번째에서 발생했는지 알아내기 어려움

- 특정 조건에 따라 분기 나누는 작업 어려움

- 특정값 공유하면서 작업 처리하기 까다로움

=> 그래서 나온 대안책 : async/await

 

async / await

- ES8에 해당하는 문법으로, Promise를 더 쉽게 사용할 수 있게 함

- 함수 선언 시 함수 앞에 async 키워드를 붙이고 promise나 특정 함수 앞 부분에 await을 넣으면 해당 작업 끝날때까지 기다렸다가 다음 작업 수행 가능

- async 함수는 결과값을 Promise로 반환

- async 함수에서 에러 발생시킬 때 throw 사용 ( ex. throw new Error(); )
   에러 잡을 때는 try/catch문 사용

- Promise.all : 여러 async 함수를 비동기로 실행하고 싶을 때 사용
   주의) 등록한 promise 중 하나라도 실패하면 모두 실패로 간주

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

const getDog = async () => {
  await sleep(1000);
  return '멍멍이';
};

const getRabbit = async () => {
  await sleep(500);
  return '토끼';
};
const getTurtle = async () => {
  await sleep(3000);
  return '거북이';
};

async function process() {
  const results = await Promise.all([getDog(), getRabbit(), getTurtle()]);
  console.log(results);		// ["멍멍이", "토끼", "거북이"]
}

process();

 

* 각 함수 내에서 콘솔을 찍어보면 비동기적으로 실행되는 것을 관찰할 수 있다.

 

- Promise.race: 여러개의 promise를 등록해서 실행했을 때 가장 빨리 끝난 하나의 결과값만 가져옴
   다른 Promise가 성공하기 전에 먼저 끝난 Promise가 실패하면 이를 실패로 간주함