비동기
- 동기: 순차적으로 코드 실행 된다.
- 비동기: 순차적으로 코드가 실행되지 않는다.
콜백 패턴
다음 코드를 보자. 출력된 결과값은 2, 1 의 순서일 것이다.
만약 1 , 2 의 순서로 출력을 하고싶다면 어떻게 해야할까?
콜백패턴을 사용하여 원하는대로 출력을 할 수 있다.
const a = (callback) => {
setTimeout(()=> {
console.log(1)
callback()
},1000)
}
const b = () => console.log(2)
a(b)
다음 코드에서 1 , 2 ,3, 4 의 순서대로 출력이 되게 하려면 어떻게 해야할까?
다음과 같은 콜백 패턴을 사용하면 된다.
콜백패턴을 사용하는 또 다른 예시를 살펴보자.
getMovies 함수는 내부에 비동기 로직이 존재한다.
따라서 아래 호출부의 코드는 동기적인 결과값이 나오지 않는다.
다음처럼 먼저 도착한 비동기 통신의 응답부터 출력된다.
다음과 같은 콜백 패턴을 사용하여 의도적으로 순서를 조정할 수 있다.
하지만 점점 콜백 패턴이 중첩되는 콜백 헬 코드가 되고 있다.
이러한 중첩이 깊어지면 코드를 이해하기 힘들어진다. 이와 같은 문제점을 해결하기위해 es6에서는 프로미스를 사용한다.
프로미스
프로미스는 비동기 처리 상태와 처리 결과를 관리하는 객체다.
프로미스를 사용하여 다음 콜백 패턴을 사용한 코드를 수정해보자.
const a = (callback) => {
setTimeout(()=> {
console.log(1)
callback()
},1000)
}
const b = () => console.log(2)
a(b)
a 함수를 호출하면 resolve 를 만나 비동기 처리 결과를 resolve 함수에 인수로 전달하면서 프로미스를 반환한다.
resolve 함수에 전달된 인수는 then 체이닝 메서드의 인수로 전달된다. 지금은 비어있다.
따라서 1 이 출력된 이후 2 가 출력되는 것을 보장할 수 있다.
그럼 다음 콜백 패턴은 프로미스를 사용하면 어떻게 바꿀 수 있을까?
then 은 프로미스를 반환하기 때문에 메소드 체이닝을 사용할 수 있다.
a()
.then(()=>b())
.then(()=>c())
.then(()=>d())
.then(()=>console.log('done'))
위 코드에서 d 함수는 프로미스를 반환하지 않는다. 하지만 then 메서드는 콜백함수가 프로미스가 아닌 값을 반환하면 암묵적으로 resolve 또는 reject 하여 프로미스를 생성해 반환하기 때문에 .then(()=>console.log('done')) 과 같은 메서드 체이닝이 가능해진다.
이제 다음 코드를 프로미스를 사용한 코드로 바꿔보자.
Tip
then 메소드 내부의 콜백함수가 실행되는 위치가 resolve() 다음 위치라고 생각하면 코드를 이해하는데 도움이 될 것 같다.
catch 같은 경우에는 reject 다음이 될 것이다.
Async / Await
프로미스의 메서드 체이닝을 사용하여 비동기를 제어하는 방식은 이전의 콜백 패턴보다 가독성 측면에서 좋다.
하지만 이 방법 또한 콜백함수를 아예 사용하지 않는 것은 아니다. 따라서 es8 에서는 비동기를 동기적인 코드처럼 다룰 수 있게하는 async await가 도입되었다.
그럼 아래의 코드를 async await 코드로 바꿔보자.
다만 await 키워드는 async 를 사용한 함수 내에서만 사용할 수 있음을 유의해야한다.
Tip
b 함수도 await 키워드가 붙은 a 함수 내부의 resolve 다음에 호출된다 라고 생각하면 코드 흐름을 이해하는데 도움이 될 수 있다.
이제 다음 코드도 async await 를 사용한 코드로 바꾸어보자.
조금 더 동기적으로 코드를 읽을 수 있는 것이 느껴지지 않는가?
Resolve, Reject 그리고 에러 핸들링
콜백 패턴으로 에러 핸들링을 할 경우에는 성공시 사용될 콜백과, 실패시 사용될 콜백을 arguments로 모두 넣어주어야한다.
아래 코드를 프로미스를 사용한 코드를 활용하여 에러핸들링 해보자.
then 과 catch 사용하여 각각 resolve , reject 된 프로미스 객체를 잡을 수 있다.
async 와 await 를 사용한 코드는 다음과 같다. try catch 문을 사용할 수 있다는 것이 유의해야할 점이다.
finally
비동기 로직의 성공 실패와 관계없이 꼭 한번 실행된다.
다음 코드 또한 에러 핸들링 해보자.
상기하고 싶은 부분은 const moives 변수에 await 를 사용한 비동기 로직의 결과 값을 할당하는데, 이때 이 결과값은 resolve 의 인자로 들어간 json 이 할당된다.
반복문에서 비동기 처리
반복문에서 비동기 처리는 어떻게 해야할까?
forEach 문 안에서 async await 를 사용하여 비동기 로직을 핸들링하려고 했지만 다음처럼 원하는 출력결과를 얻을 수 없다.
이럴때는 forEach 할 수 없고 for of 를 사용해야한다.
fetch
fetch를 사용하면 네트워크를 통해 리소스 요청 및 응답을 처리할 수 있다. fetch 함수는 Promise 인스턴스를 반환한다.
Promise 를 반환하기 때문에 then, catch, finally 모두 사용할 수 있다.
fetch 를 사용해서 응답받은 데이터를 콘솔로 찍어보면 아래와 같은 형태이고 서버에서 보낸 데이터를 참조하기 위해서는 json 메서드를 사용해야한다.
json 메서드를 사용하면 프로미스 객체를 얻을 수 있다.
이 객체를 다시한번 then 메소드를 사용하여 resolve 된 데이터를 얻는다.
위 코드를 async await 를 적용한 코드는 다음과 같다.
fetch 의 두번째 인자 , 옵션
method
GET 은 default 이기 때문에 생략해도 된다. 만약 POST , PUT, DELETE 를 사용하려면 명시해줘야한다.
headers
서버로 전송하는 요청에 대한 정보를 담고 있다.
아래 코드는 json 포맷으로 요청하겠다는 것을 의미한다.
body
요청에 대한 데이터를 담고 있다.
body에 명시하는 데이터는 문자화 시켜야한다. JSON.stringify 를 사용한다.
'카카오 테크 캠퍼스 > HTML CSS JS' 카테고리의 다른 글
[카테캠 7주차] JS 기초 Events (0) | 2023.05.28 |
---|---|
[카테캠 7주차] JS 기초 DOM (0) | 2023.05.28 |
[카테캠 6주차] 자바스크립트 기초 (1) | 2023.05.21 |
[카테캠 5주차] 자바스크립트 기초 (0) | 2023.05.14 |
[카테캠 4주차] 오버워치 캐릭터 선택 예제 (4) | 2023.05.07 |