CS/프로그래밍

await, async

오류확인자 2023. 7. 2. 12:27

1. 비동기 방식

특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 생할하는 특성을 말한다.

기다리는 동안 다른 함수를 호출할 수 있다.

 

 

2. 동기적 방식

작업이 끝날 때까지 다른 작업을 할 수 없다.

 

위 그림과 같이 비동기적과 동기적을 나타낼 수 있다.

동기적은 위그림과 같이 1번 작업이 끝나고 2번 그리고 2번 끝나면 3번 이러한 순서로 진행이 된다.

하지만 비동기적은 1번이 끝나기 전에 2번 코드를 실행 그리고 3번 4번 이러하게 진행이 가능하다.

비동기적에서 대표적인 함수는 setTimeout()가 있다.

이 코드는 바로 실행하지 않고 지정한 시간만큼 기다렸다가 로직을 실행한다.

아래와 같이 예시가 있다.

function test() {
  console.log('첫번째');
  setTimeout(() => {
  console.log('두번째 (setTimeout 함수 내부)')
  }, 1000);
  console.log('세번째');
  }
  
  test();

비동기 처리에 대한 개념이 없었다면 이러한 순서로 진행이 됐을 것이다.

1. 첫번째

2. 1초 있다가 두번째 (setTimeout 함수 내부)

3. 세번째

 

하지만 실제 순서는 위 그림과 같이 실행이 되었다.

첫번 째 실행이 되고, 두번 째는 1초 뒤에 실행이 되기 때문에, 세번 째 실행이 되고 나서 두번 째가 실행이 된다.

setTimeout()가 비동기 방식이라 이러한 결과가 나온 것이다.

 

3. 비동기 방식이 필요한 사례

  • Ajax Web API 요청(서버 쪽에서 데이터를 받아올 때)
  • 파일 읽기(서버 쪽에서 파일을 읽어와야 할 때)
  • 암호화 / 복호화
  • 작업 예약 (몇 초 후에 해야 되는 작업이 필요 할 때)

4. 비동기 방식의 종류

  • 콜백 함수
  • Promise
  • async & await

5. 콜백함수

요새는 잘 사용하지 않는 비동기 함수이다.

그 이유는 콜백함수로 처리하게 되면 비동기 작업이 많아질수록 코드가 복잡해지기 때문에, 이걸 콜백지옥이라고 한다.

간단하게 숫자 n을 파라미터로 받아서 5번에 걸쳐 1초마다 1씩 더해지는 로직이 있다고 하자.

unction callbackHell(n, callback) {
  setTimeout(() => {
    const increased = n + 1;
    console.log(increased);
    if(callback) {
      callback(increased);
    }
  }, 1000);
}

callbackHell(0, n => {
  callbackHell(n, n => {
    callbackHell(n, n => {
      callbackHell(n , n => {
        callbackHell(n, n => {
          console.log('이게 콜백지옥');
        });
      });
    });
  });
});

위와 같이 코드가 정말 난잡하다. 1초씩 더해줘야 하기 때문에 콜백의 콜백 ... 

그래서 나온 방법이 Promise 이다.

 

6. Promise

프로미스는 주로 서버에서 받아온 데이터를 화면에 표시할 때 사용한다.

서버에다 데이터를 요청하는데 데이터를 받아오기 전에 데이터를 다 받아온 것처럼 화면에 데이터를 표시하려고 하면 오류가 발생한다.

이와 같은 문제를 해결하기 위해 나온 방법 중에 하나가 프로미스이다.

 

promise의 기본 문법

const getData = new Promise((resolve, reject) => {
 // 구현 코드
});

new Promise() : 메서드를 호출할 때 콜백 함수를 선언할 수 있고, 콜백 함수의 인자는 resolve, reject이다.

resolve는 성공할 때, reject는 실패 할 때 호출한다.

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('resolve는 성공했을 때');
  }, 1000); 
});

myPromise.then(result => {
  console.log(result); // resolve 성공했을 때
});

resolve를 호출 할 때 특정 값을 파라미터로 넣어주면, 이 값을 작업이 끝나고 나서 사용할 수 있는데, 작업이 끝나고 또 다른 작업을 해야 할 때에는 promise 뒤에 .than()키워드를 붙여서 사용하면 된다.

 

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('reject는 실패했을 때');
  }, 1000); 
});

myPromise.then(result => {
  console.log(result)
})
.catch(e => {
  console.error(e); // reject는 실패했을 때
})

reject를 호출 할 때는 .catch()키워드를 사용하여 실패했을 시 수행할 작업을 설정할 수 있다.

 

promise 흐름

pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태

Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태

Rejected(실패) :비동기 처리가 실패하거나 오류가 발생한 상태

 

Promise의 단점

promise 의 단점이 있다. 에러를 잡을 때 몇번 째에서 발생했는지 알아내기가 힘들고 특정 조건에 따라 분기를 나누는 작업도 어렵다.

그리고 특정 값을 공유하면서 작업을 처리가 까다롭다. 이걸 해결하기 위해 나온 것이 async, await 다.

 

7. async / await 

async / await 최근에 나온 비동기처리 패턴으로 기존의 콜백 함수와 프로미스의 단점을 보완한다.

 

async / await 기본 문법

// ES5
async function 함수명() {
 await 비동기 처리 메서드명();
}

// ES6
const 함수명 = async() => {
 await 비동기 처리 메서드명();
}

먼저 함수 앞에 async 키워드를 붙인다. 그리고 함수 내부 로직 중 HTTP 통신을 하는 비동기 처리 코드 앞에 await를 붙인다.

주의할 점은 비동기 처리 메서드가 꼭 프로미스 객체를 반환해야 한다.

그 이유는 간단히 말해서

비동기 처리 메서드는 프로미스 객체를 반환해야 함.
await 키워드를 사용하기 위해 프로미스 객체를 기다림.
프로미스는 비동기 작업의 상태와 결과를 나타낸다.

 

예시를 봐보자.

const fetchItems = () => {
  return new Promise((resolve, reject) => {
    let items = ['Callback', 'Promise', 'async/await'];
    resolve(items);
  });
};

const Asynchronous = async () => {
  let resultItems = await fetchItems();
  console.log(resultItems);// ["Callback", "Promise", "async/await"]
};

Asynchronous();

fetchItems() 함수는 프로미스 객체를 반환하는 함수이다. fetchItem() 함수를 실행하면 프로미스가 resolve되며 결과 값은 items 배열이 된다.

Asynchronous() 함수를 실행하면 fetchItems() 함수의 결과 값인 item 배열이 resultItems 변수에 담겨 배열이 잘 출력된다.

await를 사용하지 않으면 데이터를 받아온 시점에 콘솔을 출력할 수 있게 콜백함수 .than()를 사용해야 한다.

 

async / await 예외 처리

async/await 에서 예외처리는 try catch이다. 프로미스에서 에러 처리를 위해 .catch()를 사용했던 것처럼

async에서는 catch()를 사용하면 된다.

 

const fetchUser = () => {
  let url = "https://naver.com/users/1";
  return fetch(url).than(res => res.json());
};

const fetchTodo = () => {
  let url = "https://naver.com/todos/1";
  return fetch(url).than(res => res.json());
};

const Asynchronous = async() => {
  try {
    let user = await fetchUser();
    if(user.id === 1) {
      let todo = await fetchTodo();
      console.log(todo.title);
    }
  } catch (e) {
    console.error(e);
  }
};
Asynchronous();

위 함수들을 실행하면 사용자 정보와 할 일 정보가 담긴 프로미스 객체가 반환된다.

url들을 임시이다.

사용자 아이디가 1인 유저의 할 일 정보의 제목을 콘솔에 출력하는 코드이다.

async/await 에서 예외 처리는 try catch이다. 프로미스에서 에러 처리를 위해 .catch( )를 사용했던 것처럼 async에서는 catch { }를 사용하면 된다.
위의 코드에 try catch 문법을 적용한 코드이다.

 

네트워크 통신 오류 또는 일반적인 오류까지도 catch로 잡을 수 있다.

 

출처 : https://chat.openai.com/

출처 : https://jindev-t.tistory.com/90

출처 : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

 

'CS > 프로그래밍' 카테고리의 다른 글

클로저  (2) 2023.07.16
MVC, MVP, MVVM 모델  (0) 2023.07.09
forEach()와 map의 차이점  (0) 2023.07.01
객체 지향 프로그래밍  (0) 2023.06.25
호이스팅_민희  (0) 2023.06.19