전 포스팅으로 비동기, 동기를 알아보았다.
이제 콜백과 왜 프로미스, async/await을 사용하는지 알아보도록 하자
☎️ 콜백(callback)이란??
- 콜백이란 함수(A)의 전달인자(argument)로 넘겨주는 함수(B)를 말한다.
- 매개변수를 넘겨 받은 함수(A)는 callback함수(B)를 필요에 따라
즉시 실행(동기, synchronously)할 수도 있고,
나중에 실행(비동기, asynchronously)할 수도 있다.
쉽게 이해하기 위해 예를 들어보면,
인기가 많은 식당에서 자리를 예약하는 것이라고 보면 될것이다.
예약을 해놓고 쇼핑, 산책 등등 하다가 식당에서 자리가 생기면
연락이 올것이다. 그 전화를 받는 시점이!! 콜백 함수가 호출되는 시점.
자리가 났을때 연락이 오기 때문에 기다릴 필요도 없고,
직접 식당안에 들어가서 자리가 비어있는지 확인할 필요도 없기 때문에
자리가 준비된 시점! 즉, 데이터가 준비되면 저희가 원하는 동작을 수행함.
⭐️ 동기 콜백 호출 vs 비동기 콜백 호출
동기 콜백 호출
비동기 콜백 호출
비동기 함수 전달 패턴
- callback 패턴
위 callback 함수 패턴은 order함수의 매개변수로 callback함수를 주고
3초뒤에 콜백함수가 실행되면서
주문한 피자가 나왔다는 멘트가 콘솔에 찍히게 된다.
- 이벤트 등록 패턴
⚙️ 비동기 호출 처리 방법
비동기 처리를 하는 이유는 여러가지가 있겠지만
- 비동기 함수는 결과를 예상할 수 없다.
- 각각의 task가 걸리는 시간이 다 다르기 때문이다.
- 아래와 같은 경우에서 콜백 순서를 제어하기 위해서 사용한다.
🕹️ 비동기 제어하는 방법 (콜백)
비동기 함수의 콜백 내부에서 다음작업(비동기 함수를 호출 등등)을 하는 것
함수의 매개변수로 넘겨지는 콜백 함수가 계속 반복되어
코드가 계속 깊어지면서 콜백 지옥을 만든다.
이렇게 계속 깊어진다면 가독성이 엄청 떨어지고,
코드를 수정하기가 매우 어려워진다.
🔥 콜백 지옥
- 콜백 지옥(callback hell)이란 콜백 함수를 익명 함수로 전달하는 과정에서
또 다시 콜백 안에 함수 호출이 반복되어
코드의 들여쓰기 수준이 감당하기 힘들 정도로 깊어지는 현상 - 주로 이벤트 처리나 서버 통신과 같은 비동기 작업을 제어하기 위해 사용,
이러한 코딩은 가독성이 떨어지고, 수정이 매우 어렵다.
따라서, 이제 설명하려는 것과 같이 비동기를 제어해서 콜백지옥을 탈출해야한다.
🚔 비동기를 제어하는 방법 (콜백지옥을 탈출하자!!!)
1. Promise
- Promise는 new 연산자와 함께 호출, 인자로 콜백을 받음
- Promise 는 Promise를 반환
- 중요 !! ⭐️Promise는 호출될 때 바로 실행되지만, 그 안에 콜백은
resolve, reject 둘 중 하나가 호출되기 전에는
then 또는 catch로 넘어가지않음 - resolve, reject로 성공 또는 실패로 결과 값을 나타내어 다음 작업을 체이닝(즉, 비동기 제어 가능)
- then으로 작업을 이어가려면 resolve() 함수를 호출함
- 작업을 중단해야하거나 err처리를 해야하면 reject() 함수를 호출
즉, 서버 API 등 서버의 데이터를 받아올 때 성공적으로 받아와지면 resolve() 함수를 호출하고,
실패하면 reject() 함수를 호출해서 각각 서로 다른 작업으로 이어질 수 있게 만듬
promise는 언젠가 완료되는 작업의 결괏값을 담는 상자와 같은 역할
Promise는 다음 중 하나의 상태를 가짐.
- 대기 (pending) : 이행하지도 거부하지도 않은 초기 상태
- 이행 (fulfill) : 작업 성공!!
- 거부 (reject) : 작업 실패!!
위의 코드를 보면 ,
새로운 Promise를 생성하고, "첫번째"를 출력하는 fn함수를 정의한다. Promise는 즉시 해결되고,
0초 후에 콘솔에 "두번째" 가 출력되게 하는 새로운 Promise를 생성하는 콜백과 함께
Promise에서 then 메서드가 호출되도록 트리거함.
위의 Promise가 해결되면 "세번째"를 출력하는 콜백과 함께 다음 then 메서드가 호출됨
위 코드에선 에러조건이 정의되지 않았으므로 catch 메서드는 호출되지 않음.
또 다른 패턴을 보면,
위의 예제에서는 fn() 함수가 Promise를 반환했기 때문에,
num변수도 Promise타입으로 바뀐다.
다음은, Promise를 이용하여 다음 비동기 함수로 값을 전달하는 로직이다.
위의 예제에서는 데이터를 resolve()에 인자(data)로 넣어 then으로 전달.
then에서 다음 then으로 데이터를 전달하기 위해 반드시 리턴값으로 전달해야함.
2. async / await
ES8에서 도입된 async, await를 사용하면 비동기함수를 동기적 코드인거처럼 동작하도록 구현가능
즉, 프로미스의 후속처리 메서드 (then, catch, finally) 없이
마치 동기처리처럼 프로미스가 처리결과를 반환하도록 구현가능
💥 비동기함수는 항상 promise객체를 반환함.
위의 예제를 보면 return을 하지않았어도 자동으로 fn함수는 Promise를 리턴
await를 붙인 Promise는 Promise를 리턴하지 않음(resolve에 인자로 전달한 데이터를 리턴)
비동기 작업을 수행하고자 하는 함수 앞에 async를 표시(async키워드를 통해 프로미스 반환)하고,
함수 내부에서 실질적인 비동기 작업이 필요한 위치마다 await를 표기.
그러면 뒤의 내용을 Promise로 자동 전환, 해당 내용이 resolve된 이후에 다음으로 진행.
await는 async 안에서만 작동.
await 키워드를 쓰면 해당값이 반환 되기 전까지 기다리는 동안 async 내부함수는 일시 중단.
async/await가 promise 방법보다 작성하기 쉽고, 가독성이 좋다
참고