ES6 Grammar Essentials (c) - Asynchronous

zzwwoonn·2022년 5월 5일
0

React

목록 보기
9/23

비동기 함수
비동기 함수(Asynchronous Function)는 비동기 처리를 위한 함수이다. 비동기 처리란 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 자바스크립트의 특성이다.

대표적인 예로 서버에 데이터를 요청하고 그 결과를 기다리는 과정이 있는데 가장 흔한 사례는 제이쿼리의 ajax이다. 제이쿼리로 실제 웹 서비스를 개발할 때 ajax 통신을 이용하는데 보통 화면에 표시할 이미지나 데이터를 서버(DB)에서 불러와 표시해야 하는데 이때 ajax 통신으로 해당 데이터를 서버로부터 가져올 수 있기 때문이다.

제이쿼리의 ajax 통신

코드를 통해 살펴보자.

function getData() {
	var tableData;
	$.get('https://domain.com/상품 카테고리/~~~상품', function(response) {
		tableData = response;
	});
	return tableData;
}

console.log(getData()); // undefined

getData 함수안에 $.get() 부분이 ajax 통신을 하는 부분이다. 정해진 도메인이나 서버에 HTTP GET 요청을 날려 특정 상품의 정보를 요청하는 코드이다.

그렇게 서버에서 받아온 데이터는 response 인자에 담긴다. 그리고 tableData = response; 코드로 받아온 데이터를 tableData라는 변수에 저장한다. 그러고 console을 통해 가져온 데이터를 출력해본다.

근데 결과는 undefined가 나온다... 왜 그럴까?

그 이유는 $.get()로 데이터를 요청하고 받아올 때까지 기다려주지 않고 다음 코드인 return tableData;를 실행했기 때문이다. 따라서, getData()의 결과 값은 초기 값을 설정하지 않은 tableData의 값 undefined를 출력한다.

이렇게 특정 로직의 실행이 끝날 때까지 기다려주지 않고 나머지 코드를 먼저 실행하는 것이 비동기 처리이다. ( 데이터가 다 올 때까지 기다리고 다 온게 확인이 되면 그 때 console.log를 하게 해야한다. )

JS에서 비동기 처리가 필요한 이유를 생각해보면 서버로 데이터를 요청했을 때 서버가 언제 그 요청에 대한 응답을 줄지도 모르는데 마냥 다른 코드를 실행 안 하고 기다릴 순 없기 때문이다.

또 다른 예를 살펴보면 setTimeout() 함수가 있다.

setTimeout() 함수

기존 JS는 비동기 작업을 위해 지연 작업이 필요한 함수에 setTimeout() 함수를 이용했다.

setTimeout()은 Web API의 한 종류이다. 코드를 바로 실행하지 않고 지정한 시간만큼 기다렸다가 로직을 실행한다.

그리고 지연 작업 완료 이후 실행되어야 하는 함수는 지연 작업 함수의 인자로(콜백 함수로) 전달하여 처리했다.

// #1
console.log('Hello');
// #2
setTimeout(function() {
	console.log('Bye');
}, 3000);
// #3
console.log('Hello Again');

비동기 처리에 대한 이해가 없다면 위 코드의 결과를

  1. ‘Hello’ 출력
  2. 3초 있다가 ‘Bye’ 출력
  3. ‘Hello Again’ 출력

으로 예상할 것이다. 하지만 실제로는

  1. ‘Hello’ 출력
  2. ‘Hello Again’ 출력
  3. 3초 있다가 ‘Bye’ 출력

이렇게 나온다. setTimeout() 역시 비동기 방식으로 실행되기 때문에 javascript 엔진이 먼저 코드를 실행후 비동기 이벤트인 Web API 를 실행 한다. 따라서 위와 같은 결과가 나온다.

setTimeout()의 동작 원리 - 추가 설명

설명이 정말 너무나 깔끔하다 정말 말도 안된다

예제 1

alert('A');
setTimeout(function() {
  alert('B');
},0);
alert('C');

실제 결과 : A -> C -> B

실행 과정

Step1) alert('A')를 call stack에 넣고 alert를 실핼 후 stack에서 제거

Step2) setTimeout 을 call stack에 넣고 setTimeout의 Event handler를 Event Queue에 넣은후 setTimeout을 stack에서 제거

Step3) alert('C')를 call stack에 넣고 alert를 실핼 후 stack에서 제거

Step4) callStack이 비어있을 경우 Event queue에서 Event handler하나를 call stack에 넣은 후 Event Queue에서 Event handler 하나를 제거

Step5) callStack에 존재하는 이Event handler 를 실행 후 call Stack에서 제거

예제 2

 for (var i = 0; i < 3; i++) {
 	setTimeout(function() {
    	alert(i);
 	}, 1000);
 }

실제 결과 : 3 3 3
( 분명히 ? 1, 2, 3 이라고 예상 했을 것이다. )

실행 결과에 대한 이유

javascript 엔진이 먼저 코드를 실행 후 !!! 비동기로 동작하는 setTimeout Web API 를 3번 실행 하였기 때문에 현재 Event Queue에는 3개의 Event Handler가 들어가있다. 모든 코드 실행 후 마지막으로 Event Queue에 등록된 Event Handler 가 처리가 되고? 현재 for 문의 i 값이 3임으로 실행되는 Event handler는 3을 3번 출력 하는 것이다.

< 예제 1,2의 출처 - 즐거운 인생(미련 없이 하자) - 자바스크립트의 동작 순서 >

콜백 지옥

$.get('url', function(response) {
	parseValue(response, function(id) {
		auth(id, function(result) {
			display(result, function(text) {
				console.log(text);
			});
		});
	});
});

다음과 같은 코드를 본 적이 분명히 있을 것이다. 콜백 지옥이라 하며 이는 비동기 처리 로직을 위해 콜백 함수를 연속해서 사용할 때 발생하는 문제이다.

일반적으로 콜백 지옥을 해결하는 방법에는 Promise나 Async를 사용하는 방법이 있다. Promise 와 Async에 대해서는 다음에 다시 공부해보자.

만약 코딩 패턴으로만 콜백 지옥을 해결하려면 아래와 같이 각 콜백 함수를 분리해주면 된다.

function parseValueDone(id) {
  // 2 
	auth(id, authDone);
  // 3 
}

function authDone(result) {
  // 4
	display(result, displayDone);
  // 5
}
function displayDone(text) {
  // 6
	console.log(text);
}
$.get('url', function(response) {
	parseValue(response, parseValueDone); 
  // 1
});

먼저 ajax 통신으로 받은 데이터를 parseValue() 메서드로 파싱한다. parseValueDone()에 파싱 한 결과값인 id가 전달되고 auth() 메서드가 실행된다. auth() 메서드로 인증을 거치고 나면 콜백 함수 authDone()이 실행된다. 인증 결과 값인 result로 display()를 호출하면 마지막으로 displayDone() 메서드가 수행되면서 text가 콘솔에 출력된다.

0개의 댓글