본문 바로가기

Web Dev/JS

JS - 자바스크립트 비동기

자바스크립트 비동기를 위해 알아야한 키워드

 

* 동기(sync) & 비동기(async)

ex) 1번, 2번, 3번이라고 부르는 세 가지 일이 있다고 하자

동기 - 1번 작업이 끝나면 2번 작업을 시작, 2번 작업이 끝나면 3번 작업이 시작

비동기 - 1번, 2번, 3번 작업이 동시에 진행됨

 

* 비동기 작업을 위한 API의 예시 - 브라우저 또는 node.js가 아래의 API를 제공해준다. (순수 자바스크립트에는 비동기 API가 없다한다)

Ajax(비동기로 통신하기 위한 api. fetch함수가 바로 ajax작업을 위한 공식 api. axios는 더 쉽게 ajax를 하기 위한 라이브러리)

파일 읽기

암호와/복호와

작업 예약(setTimeout()과 같은 것)

 

* 비동기 작업이 가질 수 있는 3가지 상태 - pending, fulfilled, rejected

단순히 Promise가 이러한 상태들을 먼저 정의했다기 보다는 일반적으로 생각해볼 때 비동기 작업은 이러한 세 가지 상태를 가진다고 볼 수 있기에 Promise에서 이 상태들을 이용해 로직 분리를 쉽게해주어 callback을 이용한 처리를 쉽게 해준 것 같다.

하나의 콜백안에 이 세 가지 상태의 처리 로직이 다 들어가 있는데, 또 콜백의 콜백의 콜백을 호출하니 지옥이 될 수 밖에 없는 것... ㄷ ㄷ 그래서 Promise는 똑똑하게 상태에 따른 로직 분리를 해주고 상태에 맞게 콜백 처리를 해준다!

 

ex) 오케이 나 약속할게요. 데이터 끌어오기 작업을 A 서버에 이렇게 할거구요. 성공하면 서버로부터 받은 data를 넣어서 resolve(data) 함수를 호출할게요. 그런데 실패한다면 error 객체를 넣어서 reject(error) 함수를 호출할게요. OK?

성공할 경우 data를 결과값으로 보내줄꺼니까 data를 인자로 받을 콜백 함수를 .then(callback(data))와 같이 넣어서 처리해줘요. 실패할 경우에는 error객체를 결과값으로 보내줄꺼고 errror 객체를 인자로 받을 콜백 함수를 .catch(callback(error))와 같이 넣어서 처리해주면 되구요. 간단하죠? ㅎㅎ

 

* 비동기 처리의 진화

1단계 - 콜백(callback) - 가장 기본적인 방법. fn(callback(a)){callback()}asdfasdfasdf 무튼 복잡함.

 

함수의 인자로 보내주는 함수를 칭하며, 보통 비동기 API 함수의 인자로 보내어 함수 내부의 특정한 지점에서 특정한 처리를 해주기 위해 사용. 다시 말해, 비동기로 실행한 작업의 결과값을 받아 다시 비동기 작업을 실행하거나 또는 동기적인 작업을 실행하는 등의 결과값 처리를 해주기 위해서!

 

콜백함수 내에서 다시 비동기 API 함수를 사용하고 비동기 API 함수를 위한 콜백을 인자로 보내주는 경우 콜백 지옥이 발생!

 

 

2단계 - 프로미스(Promise) - 콜백지옥 빡치네.. 일단 비동기 작업이 미래에 어떤 결과 가져올지 모르니까 진행, 성공, 실패 상태로 로직 분기하고 성공 때 resolve, 실패 때 reject 함수 호출해주기로 약속할께ㅇㅇ.

그리고 성공하면 .then(callback(arg))을 이용해 콜백 처리할꺼야! 실패하면 .catch(callback(arg))통해 할꺼구! 콜백 지옥 ㅂ2.

 

콜백 지옥을 해결하고 비동기 작업을 또는 비동기 api를 동기적인 모습으로 다루기 위해 Promise 객체가 도입됨(es6). promise라는 이름에서 유추할 수 있듯이 Promise객체 생성시 보내주는 콜백함수 안에서 resolve, reject 함수를 인자로 받아 비동기 작업을 처리하는 내부 로직에 따라 성공시 resolve(arg) 또는 실패시 reject(arg) 함수를 호출하겠다고 약속해 놓는 것이다.(성공과 실패는 자기가 구현하기에 따라 어떤 조건에서는 resolve를, 어떤 부분에서는 reject를 호출하면 되는 것이다. arg는 서버로부터 받은 데이터나 Error객체 등이 될 수 있겠다.)

이런 약속을 통해 resolve가 호출되는 성공시 .then(n=>{}), reject가 호출되는 실패시 .catch(n=>{})를 이용하여 동기적인 모습으로 비동기 작업을 처리할 수 있게 해주는 것이 Promise 객체이다.

 

ex) getMyDataFrom('http://tonyw.com/mydata').then(data => {console.log(data)}).catch(error => {console.error(error)})

getMyDataFrom함수를 실행하고 성공하면 데이터를 받아서 콘솔 출력해줘!. 근데 실패하면 에러 객체를 콘솔 출력해줘ㅠ.

p.s) Promise는 pending, fulfilled, rejected의 세 가지 상태를 가진다.

 

 

3단계 - async, await - .then() .catch() 안에 콜백 넣어서 쓰면 여전히 다양한 로직 구현은 어려움 ㅡㅡ. 비동기 처리 결과값 줄 때까지 기다리고 결과값 받으면 변수에 넣어 일반적으로 동기 코드 쓰듯이 동기적으로 처리할꺼임 ^^ 꿀 :)

 

프로미스를 통해 콜백 지옥을 벗어낫지만 프로미스도 .then().then().then()과 같이 .then()이 길어지면 마찬가지로 복잡해보이고 조건에 따른 분기 작업도 어려웠으며 중간의 .then()에서 에러가 난 경우 어느 .then()에서 에러가 발생한 것인지 디버깅이 어려운 단점이 있었다.

 

따라서 es8에서는 async, await 키워드가 도입되어 이 문제를 해결 했으며 promise를 이용한 방식보다도 더 동기 함수와 같은 모습으로 비동기처리를 할 수 있게 되었다. Promise에서 콜백 지옥을 벗어나 좀 더 동기적인 모습으로 콜백을 사용할 수 있게 해주었고, async, await에서는 Promise도 사용하면서도 콜백 함수 없이 비동기 함수의 결과값을 완전한 동기적인 모습으로 처리 할 수 있게 된 것이다. 콜백함수를 사용한다는 것은 비동기적 작업을 처리해줄 때 사용한다는 느낌이 강한데, async, await에서는 콜백을 사용하지 않기 때문이다!

 

ex) 서버로부터 데이터를 받아오는 비동기작업을 처리하는 예(위의 promise 예제를 async, await 방식으로)

async function handleDataLikeSync() {
	// await 키워드는 Promise 객체를 반환하는 함수 앞에 사용할 수 있다.
	// await 키워드가 있으면 await 함수 뒤에 호출된 비동기 함수의 최종 결과값을 반환 받을 때까지 기다린다.
	const data = await getMyDataFrom('http://tonyw.com/mydata');
	
    // getMyDataFrom(...) 함수가 최종 결과값(data)을 받아온 후, 아래 콘솔출력이 호출된다.
    console.log(data);
    
    // 비동기 함수의 처리를 동기적인 형태로 수행 할 수 있는 것이다. 이로써 우리가 사고하기 편한대로 직관적으로 로직 구현이 가능해진다.
    
    return true;
}

// async 함수는 promise 객체를 반환하여 .then을 사용할 수 있다.
handleDataLikeSync().then(flag => {console.log(flag)})

 

p.s) async, await에서는 try, catch 구문으로 에러를 핸들링한다.

 

요약

비동기 처리 어떻게 할까?

-> 1단계 - 콜백함수를 쓰자!

-> 2단계 - 프로미스로 성공, 실패 분기 처리하기로 약속해줘서 더 쉽게 콜백함수 쓰자!

-> 3단계 - async & await로 콜백함수 버리고 동기적으로 처리하자 :)

 

심화

이벤트 루프를 이용하는 자바스크립트의 동작 구조

'Web Dev > JS' 카테고리의 다른 글

JS - 객체를 생성하는 3가지 방법  (0) 2020.07.14