JavaScript

Asynchronous

2023. 3. 19. 21:52
목차
  1. 🍅프로미스 체이닝
  2. 🍅async await 
  3. 🍅 async await 생성법

🍅동기와 비동기

실행 요청을 하고 결과를 기다리는 주체가 누구인가에 따라 동기, 비동기로 나뉜다.

동기는 요청을 한 주체가 결과를 받고 비동기는 요청을 한 주체가 아닌 다른 주체가 결과를 받는 것이다.

from 냠냠맨

- 이부분은 실행컨텍스트를 공부하고 나면 이해할 수 있는 부분이라 가볍게 상기 시키고 넘어가 본다.

 

동기처리 : 특정 코드의 실행이 완료될 때까지 기다리고 난 후 다음코드를 실행하는 것

비동기처리 : 특정 코드의 실행이 완료될때까지 기다리지 않고 다음 코드를 실행하는것

- 여기서도 블로킹/논블로킹 개념이 있으나 실행컨텍스트 이해가 필요하다

 

동기 처리는 실행순서가 보장되지만 속도가 느리고

비동기 처리는 속도가 빠르지만 실행순서가 보장되지 않는다.

console.log("바로 나타났으면 좋겠당");

setTimeout(() => {
  console.log("2초 후에 만들어졌으면 좋겠다");
}, 2000);

console.log("이것도 바로 나타났으면 좋겠당");

// 바로 나타났으면 좋겠당
// 이것도 바로 나타났으면 좋겠당
// 2초 후에 만들어졌으면 좋겠다

비동기 함수 : 함수 내부에 비동기로 동작하는 코드(콜백 함수)를 포함한 함수 ex) setTimeout, addEventListener

 

동기적으로 실행되었다면 마지막 console.log는 2초를 기다리고 난 후 마지막으로 실행되었을 것이다.

하지만 자바스크립트는 런타임환경에서 비동기적으로 실행되기 때문에 먼저 처리할 수 있는 일을 수행한다.

 

🍉 콜백함수의 문제점

1. 외부반환과 상위스코프에 접근이 불가능하다.

let a = 1;

setTimeout(() => {
  a = 2;
}, 100);

console.log(a) // 1

비동기함수인 setTimeout이 먼저 호출되었지만

먼저 처리할 수 있는 console.log()를 먼저 호출한다.

 

따라서 setTimeout 함수 안에 있는 콜백함수인 a = 2가 실행되지 않고

a는 원래의 값인 1이 출력된다.

 

이처럼 비동기 함수는 처리결과를 외부에 반환할 수 없고 상위스코프의 변수에 할당할 수 도 없다.

 

2. 실행순서를 보장할 수 없다.

실행순서가 보장이 안된다
가독성이 떨어진다

예를들어 알파벳 A-Z까지 출력한다? 가독성 떨어짐

이외에도 예외처리하기도 곤란하다.

 

✔️프로미스 생성법

- 서버에서 데이터를 받아오는 등 실행하는데 오래걸리는건 비동기적으로 하는게 좋다. 

- 비동기는 실행순서가 보장되지 않기때문에 콜백함수를 전통적으로 많이 사용했지만 위에서 봤듯이 여러가지 불편함으로인해

- ES6부터 promise개념이 등장했다.

 

const promise =  new Promise()

 

Promise 생성자 함수는 비동기 처리를 수행할 콜백함수를 인자로 전달 받는데

이 콜백함수는 resolve와 reject 함수를 인수로 전달 받는다.

const promise = new Promise((resolve, reject) => {
  if(/*비동기처리 성공*/){
    resolve('result')
  } else { //비동기처리 실패
    reject('failure reason')
  }
});

비동기 처리를 성공하면 resolve를 호출하고 실패하면 reject 호출을 한다.

 

new 연산자와 함께 호출을 하면 Promise객체를 생성한다.

Promise 객체는 state, result 프로퍼티를 갖는데 직접 접근할 수 없고 메서드를 이용해 접근할 수있다.

 

기본적인 상태는 pending

비동기 처리를 수행할 콜백함수가 성공적으로 작동했다면 fulfilled, 에러발생하면 rejected가 된다.

 

✔️프로미스 후속처리 메서드

비동기 처리 상태가 변화하면 이에 따른 후속처리를 해야한다.

성공했으면 성공 처리 결과를 실패했으면 에러처리를 해줘야한다.

 

프로미스의 비동기 처리 상태가 변화하면 후속 처리 메서드에 인수로 전달한 콜백함수가 선택적으로 호출된다.

 

then 

두개의 콜백함수를 인수로 전달 받는다.

첫 번째는 비동기 처리가 성공했을때, 두 번째는 비동기 처리가 실패했을 때 호출

 

catch

한개의 콜백함수를 인수로

프로미스가 reject 상태일 경우에만 호출

 

finally

한개의 콜백함수를 인수로

프로미스의 상태와 상관없이 무조건 한번 호출된다.

 

 

const promise = new Promise((resolve, reject) => {
  const a = 1;
  const b = 2;
  if (a + b > 2) {
    resolve("success");
  } else {
    reject("failure");
  }
});

//a+b 는 3이므로 resolve 호출

promise //
  .then((tomato)=>console.log(tomato)) // success
  .catch(console.log)
const promise = new Promise((resolve, reject) => {
  const a = 1;
  const b = 2;
  if (a + b < 2) {
    resolve("success");
  } else {
    reject("failure");
  }
});

//a+b 는 3이므로 reject 호출

promise //
  .then(tomamto => console.log(tomato)) 
  .catch(console.log)//failure

 

만약에 .catch를 써주지 않으면?

uncaught 에러가 뜬다

 

const condition = true;

const promise = new Promise((resolve, reject) => {
  if (condition) {
    resolve("success");
  } else {
    reject("failure");
  }
});

promise //
  .then((msg) => {
    console.log(msg);
    return new Promise((resolve, reject) => {
      resolve(msg);
    });
  })
  .then((msg2) => {
    console.log(msg2);
    return new Promise((resolve, reject) => {
      resolve(msg2);
    });
  })
  .then((msg3) => {
    console.log(msg3);
  })
  .catch(console.error);

// success
// success
// success

중간에 에러가 난다면?

promise //
  .then((msg) => {
    console.log(msg);
    return new Promise((resolve, reject) => {
      resolve(msg);
    });
  })
  .then((msg2) => {
    return new Promise((resolve, reject) => {
      reject(msg2);
    });
  })
  .then((msg3) => {
    console.log(msg3);
  })
  .catch(console.error);

msg3가 실행이 안되고 바로 catch 가 실행이된다.

 

🍅프로미스 체이닝

const fetchNumber = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 1000);
});

fetchNumber
  .then((num) => num * 2)
  .then((num) => num * 3) // then은 값을 바로 전달할 수도 있고
  .then((num) => {
    //Promise를 전달할 수도있다.
    return new Promise((resolve, reject) => {
      setTimeout(() => resolve(num - 1), 1000);
    });
  })
  .then((num) => console.log(num)); //5

 

 

 

🍅async await 

그런데 콜백함수만 사용하는것 보다 더 간결해 보였던 프로미스에도 문제가 있다.

순서보장이 안되는 경우가 있고 then지옥에 갇힐 수 있다...... 왜 ㅠ 대체 왜 ㅠ

그래서 ES8부터 async await이 등장했다.

 

- 프로미스의 후속 처리 메서드없이 마치 동기 처리 처럼 프로미스가 처리 결과를 반환하도록 구현할 수 있다.

 

🍅 async await 생성법

- await 키워드는 반드시 async 함수 내부에서 사용해야 한다.

- async 함수는 async 키워드를 사용해 정의하며 언제나 프로미스를 반환한다.

- async함수가 명시적으로 프로미스를 반환하지 않더라도 async함수는 암묵적으로 반환값을 resolve하는 프로미스를 반환한다.

- 프로미스의 후속처리 없이 마치 동기 처리처럼 프로미스가 처리 결과를 반환하도록 구현할 수 있다.

function fetchUser() {
  return new Promise((resolve, reject) => {
    resolve("success");
  });
}

이 promise 생성함수를 

async function fetchUser() {
  return "success";
}

함수명 앞에 async를 붙여주면

async는 return 값인 "success"를 암묵적으로 resolve하는 프로미스를 반환한다.

// 함수 선언식
async function funcDeclarations() {
	await 작성하고자 하는 코드
	...
}

// 함수 표현식
const funcExpression = async function () {
	await 작성하고자 하는 코드
	...
}

// 화살표 함수
const ArrowFunc = async () => {
	await 작성하고자 하는 코드
	...
}
function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
function getBanana() {
  return delay(3000) //promise delay를 리턴
    .then(() => "🍌");
}
async function getBanana() {
  await delay(3000);
  return "🍌";
}
function pickFruits() {
  return getApple().then((apple) => {
    return getBanana() //
      .then((banana) => `${apple} + ${banana}`);
  });
}
async function pickFruits() {
  const apple = await getApple();
  const banana = await getBanana();
  return `${apple}+${banana}`;
}

🫐 async await 에서의 에러처리

async function nana() {
  try {
    const a = await new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(1);
      }, 2000);
    });

    const b = await new Promise((resolve, reject) => {
      reject("Error!");
    });

    const c = await new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(3);
      }, 1000);
    });
    console.log([a, b, c]);
  } catch (err) {
    console.error(err);
  }
}

nana(); // Error!

위 함수에서 catch문은 HTTP 통신에서 발생한 네트워크 에러 뿐만 아니라 try 코드 블록의 모든 문에서 발생한 일반적인 에러까지 모두 캐치할 수 있다.

 

- async 함수 내에서 catch 문을 사용해서 에러 처리를 하지 않으면 async함수는 발생한 에러를 reject하는 프로미스를 반환한다.

즉, Promise.prototype.catch 후속처리 매서드를 사용해 에러를 캐치할 수도 있다.

'JavaScript' 카테고리의 다른 글

React State & Props  (2) 2023.03.24
React SPA React Router  (4) 2023.03.23
프로토타입 체인  (0) 2023.03.15
프로토타입과 클래스  (0) 2023.03.15
객체 지향 프로그래밍  (2) 2023.03.15
  1. 🍅프로미스 체이닝
  2. 🍅async await 
  3. 🍅 async await 생성법
'JavaScript' 카테고리의 다른 글
  • React State & Props
  • React SPA React Router
  • 프로토타입 체인
  • 프로토타입과 클래스
Summer.dev
Summer.dev
프론트엔드 개발자 Summer 입니다! 피드백은 언제나 환영입니다.
Summer.dev
꾸준함이 무기
Summer.dev
전체
오늘
어제
  • 분류 전체보기
    • Projects
      • Next.js board-project
      • MOMO
    • 원티드
    • 우테코 프리코스
    • JavaScript
    • React
    • TypeScript
    • Node.js
    • Algorithm
      • 코플릿
      • 개념정리
    • 네트워크
    • 오류해결
    • 회고
    • 기술면접준비
    • git,github
    • 소소하게 궁금한것
    • Next.js Beta Docs 번역
    • 디자인패턴
    • 트러블슈팅
    • 번역

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 메모이제이션
  • 알고리즘

최근 댓글

최근 글

hELLO · Designed By 정상우.
Summer.dev
Asynchronous
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.