🤹🏻‍♀️ Javascript

자바스크립트 비동기 처리 방식

ji-hyun 2022. 1. 2. 17:49

동기식 (Synchronous) / 비동기식 (Asynchronous)

  • 동기식

먼저 시작된 하나의 작업이 끝날 때까지 다른 작업을 시작하지 않고 기다렸다가 다 끝나면 새로운 작업을 시작하는 방식

 

 

  • 비동기식

동기식 방식과는 다르게 먼저 시작된 작업의 완료 여부와는 상관없이 새로운 작업을 시작하는 방식

작업의 순서가 확실하지 않아서 나중에 시작된 작업이 먼저 끝나는 경우도 발생

 

 

 

 

 

< 자바스크립트 비동기 처리 >

자바스크립트는 기본적으로 비동기적으로 동작을 한다.
개발하다 보면 비동기식으로 동작하는 부분이 동기적으로 동작해야 하는 경우가 생긴다.
이럴 때 비동기식 동작이 동기식으로 동작하도록 해주는 걸 비동기 처리라고 한다.

 

 

1. 콜백함수 사용

2. promise

3. promise 를 활용한 asyne/await

 

 

 

 

 

 

 

 

콜백함수를 남용하게 되면 가독성과 에러 처리 등에서 불편함이 발생한다.

이를 해소하기 위해 ES6 에서 비동기 처리의 새로운 방법으로 promise 객체 가 등장한다.

 

promise 객체는 비동기 작업의 최종완료(또는 실패)와 그 결과 값을 나타낸다.

 

new Promise() 객체를 생성하여 콜백 함수를 선언할 수 있는데 이때의 인자는 resolve, reject 를 사용한다.

resolve 는 결과가 성공인 promise 객체를 반환하고 reject 는 결과가 실패인 promise 객체를 반환한다.

반환된 promise 객체를 처리할 때..... 성공 시 then 을 사용하고 실패시 catch 를 사용하여 처리

 

 

성공시, resolve, then

실패시, reject, catch

 

 

 

 

정리 :

promise 객체!!!!!를 생성하고 인자는 resolve , reject 사용

resolve 는 결과가 성공인 promise 객체!!!!!를 반환한다.

반환된 객체 처리는 then 처리

 

 

 

let myFirstPromise = new Promise((resolve, reject) => {
	setTimeout(function(){
    // 성공 시 resolve 사용
    resolve('Success!');
    }, 3000);
});

// 성공 시 then 사용하여 결과 처리
myFirstPromise.then((successMessage) => {
	console.log('Yay!' + successMessage);
});


// 결과
// (3초 대기)
// Yay! Success!
// 종료

 

let myFirstPromise = new Promise((resolve, reject) => {
	setTimeout(function(){
    // 실패 시 reject 사용
    reject(new Error('Fail'));
    }, 3000);
});

// reject 를 사용하여 promise 객체를 반환하기 때문에 then 은 실행되지 않음
myFirstPromise.then((successMessage) => {
	console.log('Yay!' + successMessage);
})
// 실패 시 catch 사용하여 결과 처리
.catch((reson) => {
	console.log('여기서 거부된 프로미스(' + reason + ')를 처리하세요');
});


// 결과
// (3초 대기)
// 여기서 거부된 프로미스(Error: Fail) 를 처리하세요
// 종료

 

 

 

 

 

 

 

 

그렇다면 Promise 방식이 콜백 함수보다 좋은 점이 무엇일까?

 

function a() {
	return new Promise({
    // ...
    });
}

function b() {
	return new Promise({
    // ...
    });
}

function c() {
	return new Promise({
    // ...
    });
}

myFirstPromise()
.then(a)
.then(b)
.then(c);

 

콜백 함수를 사용했으면 훨씬 길어지고 복잡해졌을 코드도 위 예시와 같이 Promise 객체를 반환하면 여러 개의 then을 연결하여 간단히 처리할 수 있다.

 

 

 

 

 

 

Promise를 활용한 async/await

ES2017에 새로 추가된 async/await 는 위에서 알아봤던 Promise 객체를 기반으로 사용한다.

 

 

 

 

기존 Promise와의 차이점

가장 큰 차이점은 위에서 배웠던 resolve, reject, then, catch를 쓰지 않는다는 것이다.

아니! 위 내용 익히고 오라더니 열심히 공부하고 오니까 안 쓴다고요?????????

사실 async/await만 사용하려면 저 4가지는 어떻게 사용했는지 잊어버려도 된다.

 

 

 

 

async

  • async 사용 예시
// async 키워드만 붙이면 된다.
async function hello() {
  return 'Hello';
}

function callHello() {
  const r = hello();
  console.log(r); 
}

callHello();

 

  • 결과
# 시작
Promise { 'Hello' }
# 끝

 

놀랍게도 위에서 사용했던 new Promise()고 뭐고 아무것도 없다.
그냥 기본 메소드 앞에 async만 붙이면 반환되는 값이 Promise 객체가 된다.
Promise 객체가 무엇인지는 이미 익히고 온 여러분은 이 간단한 예시만으로 async를 어떻게 쓰는지 이해가 다 됐을 거라 생각된다.

그런데 우리는 위 예시처럼 객체 형태의 Promise { 'Hello' }가 아니라 그 안에 있는 String 형태의 Hello를 출력하고 싶다.

어떻게 해야 할까??
다음 설명을 보도록 하자.

 

 

 

await

이제 우리는 async를 어떻게 쓰는지 또 어떤 결과가 나오는지는 알고 있다.
이번엔 그 결과를 어떻게 내가 원하는 방식으로 사용할지를 알아보도록 하겠다.

 

  • await 사용 예시
async function hello() {
  return 'Hello';
}

// (2) 새로 추가된 async 키워드
async function callHello() {
  // (1) 새로 추가된 await 키워드
  const r = await hello();
  console.log(r);
}

callHello();

 

  • 결과
# 시작
Hello
# 끝

 

 

자 달라진 내용을 살펴보자.
먼저 (1) 부분을 보면 hello(); 앞에 await 키워드가 추가됐다.
await가 붙으면 반환된 Promise 객체에서 실제 hello() 메소드의 반환 값인 String 값을 뽑아낸다고 보면 된다.
그렇다면 위 예시의 r 값에 String 형태의 Hello가 입력될 것이다.
출력 결과를 보면 await가 붙기 전이랑 다르게 r을 출력하면 문자열만 출력되는 걸 확인 할 수 있다.

다음은 (2) 부분을 보면 callHello() 메소드에도 async가 추가됐다.
await 키워드는 async 키워드가 붙은 메소드에서만 사용할 수 있다.
만약 async가 없는 메소드에서 await를 사용한다면 아래와 같은 SyntaxError가 발생한다.

 

 

 

 

 

 

 

결과 처리방법

위 내용은 굉장히 쉬워서 이해하는 데에 어려움이 없었을 것 같다.

하지만 아직 끝난 게 아니다.

이제 우리는 성공과 실패에 대해 처리를 해서 마무리를 지어줘야 하는데 기존 Promise 방식으로 생각하면 then, catch 부분을 처리해줘야 한다.
당연히 이 부분도 훨씬 쉽다.

위에서 사용한 예시를 활용하여 바로 알아보자!

 

 

  • 성공 처리 예시
async function hello() {
  return 'Hello';
}

async function callHello() {
  // 새로 추가된 try 키워드
  try {
    const r = await hello();
    console.log('성공: ' + r);
  } catch (e) {
    console.log('실패: ' + e.message);
   }
}

callHello();

 

  • 결과
# 시작
성공: Hello
# 끝

 

 

  • 실패 처리 예시
async function hello() {
  throw new Error(`Fail!`);
}

async function callHello() {
  try {
    const r = await hello();
    console.log(r);
    // 새로 추가된 catch 키워드
  } catch (e) {
    console.log(e.message);
   }
}

callHello();
  • 결과
# 시작
실패: Fail!
# 끝

 

 

기존 then과 catch는 각각 try-catch로 바뀌었다.
위 예시와 같이 await를 붙인 메소드의 결과가 성공일 경우는 try 부분이 실행되고 실패일 경우는 catch 부분이 실행된다.
이 내용은 기존 C언어나 Java언어 등에서 사용하는 try-catch 개념과 비슷하기 때문에 별다른 설명 없이도 충분히 이해가 될 거라 생각한다.

여기까지 자바스크립트의 3가지 비동기 처리 방법에 대해서 알아봤다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

참고 블로그

https://velog.io/@change/JavaScript-asyncawait%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C#%EB%8F%99%EA%B8%B0%EC%8B%9D-synchronous--%EB%B9%84%EB%8F%99%EA%B8%B0%EC%8B%9D-asynchronous

 


공식 문서 개인정리...

 

 

 

먼저 비동기 함수를 async 함수로 만들기 위하여 function() 앞에 async 키워드를 추가한다.

async function() 은 await 키워드가 비동기 코드를 호출할 수 있게 해주는 함수이다.

 

 

function hello() { return "Hello" };
hello();

위의 함수는 "Hello" 를 반환한다.

 

그러나 앞에 async 키워드를 추가하면

이제 코드가 Promise 를 반환한다....!!

 

이것이 async 특징 중 하나이다.

 

 

이렇게 작성할 수도 있다.

 

let hello = async fuction() { return "Hello" }
hello();

 

 

 

 

화살표 함수를 사용하면 아래처럼 쓸 수 있다.

 

let hello = async () => { return "Hello" }

 

 

 

그리고 실제로는 fulfil Promise 가 반환되기 때문에 반환된 값을 사용하기 위해선

.then() 블럭을 사용해야 한다.

 

hello().then((value) => console.log(value))

 

 

짧게 표현하면 아래와 같이 쓸 수 있다.

hello().then(console.log)

 

 

정리하면 async 를 함수와 깉이 사용하면 결과를 직접 반환하는게 아니라 Promise 를 반환하게 된다.

또한 동기식 함수는 await 사용을 위한 지원과 함께 실행되는 잠재적인 오버헤드를 피할 수 있다. 

 

 

 

 

 

 

 

 

 

'🤹🏻‍♀️ Javascript' 카테고리의 다른 글

moment 참고자료  (0) 2022.03.06
Ajax, Axios, Fetch  (0) 2022.01.31
객체 순회하기  (0) 2021.11.06
클래스, const 객체, 메서드, map 메서드  (0) 2021.11.06
제로초 자바스크립트 7장 (가위바위보)  (0) 2021.10.24