피리부는 사나이 (pied piper)

[JS] 대표 비동기 3가지 [콜백, 프라미스, async/await] 본문

Java Script

[JS] 대표 비동기 3가지 [콜백, 프라미스, async/await]

코더 451 2022. 5. 2. 16:31

자바스크립트 언어내에서 비동기를 가능케하는 대표적 3가지 방법 

 

콜백 :

자바스크립트에서는 함수의 인자 값으로 함수를 이용할 수 있다.

인자를 통해서 바깥에 있는 함수를 내부에서 실행시키는 것이다. 그러므로 콜백이라는 것은 다른 함수에 불러오는 함수다.

그리고 그것은 두 번째 함수를 실행 시킬 것이다.

 

콜백함수의 예시를 보자
function printString(){
   console.log("Tom"); 
   setTimeout(function()  { console.log("Jacob"); }, 300); 
  console.log("Mark")
}

printString();

만약 이 코드가 일반적으로 동기적 코드라면 아래 순서대로 나와야할 것이다

 

Tom => Jacob => Mark (동기적 순서)


그러나 setTimeout은 비동기적 함수이므로 아래와 같은 코드를 출력할 것이다.

 

Tom => Mark  => Jacob

 

왜 이런 현상이 일어났을까? 자바 스크립트에는 일정 시간동안 뒤에 ‘문'을 실행하는 “setTimerout”이라고 불리는 내장 메서드가 쓰였기 때문이다. 

 

비동기란? 먼저 지시된 작업이 끝나기까지 기다리지 않고 다음 작업을 먼저 실행하게 하는 것을 비동기 방식이라고 이야기한다.

 

 

비동기 적 코드 구현 예시

 

function taskA(a, b, cd) {
	setTimeout(() => {
	console.log("A TASK END");
	), 2000);
};

function taskB(a, cb) {
setTimeout(() => {
 const res = a * 2;
 cb(res);
	}, 1000);
}


function taskC(a, cb) {
setTimeout(() => {
 const res = a * -1;
 cb(res);
	}, 2000);
}




taskA(3, 4 (res) => {
console.log("A TASK RESULT : ", res);
});

taskA(7,  (res) => {
console.log("B TASK RESULT : ", res);
});


taskC(14,  (res) => {
console.log("C TASK RESULT : ", res);
});





console.log("코드 끝");//



// 코드끝 (먼저 출력)
// B TASK RESULT : 14
// C TASK RESULT : -14
// A TASK RESULT : 7

의도적으로 동기적 코드 호출 순서를 비동기적으로 호출하게 만들었다. 

 

그러나 이러한 전통적인 방식으로는 단점이 하나 있다

 

1. 코드가 매우 길어져서 가독성이 떨어지는 것 

2. 마찬가지로 이른바 '콜백 지옥'에 빠져서 함수가 계속 안쪽으로 파고드는 현상이 일어나서 가독성이 또한 떨어지는 것 

 

 task (4, 5 (a_res) => {
	console.log("A RESULT : ", a_res);
		taskB(a_res, (b_res) => {
		 console.log("B RESULT : ", b_res);
			taskC(b_res, (c_res) => {
				console.log("C RESULT: ", c_res);
			taskC(b_res, (c_res) => {
				console.log("C RESULT: ", c_res);//
					taskC(b_res, (c_res) => {
						console.log("C RESULT: ", c_res);//
				 });
				});
			 });
			});//
		 });
		});
	});

console.log("코드 끝")//


// 코드끝
// A RESULT : 9
// B RESULT : 10
// C RESULT : -18

이렇게 된다면 코드를 읽는데 점점 매우 어려움을 겪기 시작할 것이다. 그래서 이러한 상황을 다루기 위해 ‘프라미스'라는 객체를 사용한다면 훨씬 좋게 ‘중첩 함수'를 다룰 수 있을 것이다.

 

어떻게 중첩 함수의 콜백 지옥과 갈수록 끔직해질 코드 가독성과 이해도를 높일 수 있을까?

그것은 바로 아래부터 나온다. 


프라미스 (Promise) (이럴 때 필요한 것이 '프라미스')

: 자바스크립트 비동기 처리에 사용되는 객체, 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냄 

 

우리는 프라미스가 아니라 프로미스로 발음하는데 익숙하다, 우리말로 번역하면 말 그대로 '약속'

 

 

자바스크립트 내에 프라미스는 실제 인생과 유사하다.

우리가 현실에서 약속 (프라미스)를 잡으면, 우리가 미래에 무언가를 할 것이라는 것을 보증하는 것이듯.

프라미스는 미래를 위해서만 만들어질 수 밖에 없다.

 

프라미스는 두 가지 가능한 결과를 가진다.

이것은 때가되면 이행되거나 아니면 지켜지지 않거나 자바스크립트 내에서의 프라미스도 마찬가지다.

우리가 자바스크립트내에서 프라미스를 정의하면, 그것은 때가되면 resolve되거나 rejected 되는 두 가지 가능성만을 낳는다.

이것은 마치 if 조건문과 비슷하게 들린다. 그러나 사실 조건문과는 큰 차이가 좀 있다.

 

프라미스는 비동기적 작업의 결과를 처리하는데 쓴다.

자바스크립트는 기본적으로 전적으로 실행될 때 다른 동기적 코드 부분들이 시작 할 수 있기 전에, 실행해야 할 비동기적 코드 블럭을 기다리지 않는 것으로 설계가 되어있다.

 

그러나, 프라미스 객체와 함께라면, 우리는 동기적 요청이 완료될 때 까지 코드 블럭의 실행을 연기시킬 수 있다.

이러한 방법에 의하면, 다른 작업들이 방해없이 지속적으로 실행될 수 있다.

 


프라미스의 3가지 상태

 

  • Pending : 초기 상태, 프라미스가 성공 혹은 실패 전
  • Resolved : 이행된 프라미스 상태
  • Rejected : 실패된 프라미스 이행, 에러 도출

예를 들어 우리가 프라미스를 이용해서 서버로 부터 데이터를 요청할 때, 이는 우리의 데이터를 받기전에 Pending 모드가 된다.

만약 우리가 서버로부터 정보를 받는데 성공했다면, 프라미스는 성공적으로 resolved되었다고 할 것이고, 반대로

우리가 정보를 얻지 못했다고 한다면 프라미스는 rejected 상태를 나타낼 것이다.

 

 


 

 

프라미스 생성하기

:첫 번째로, 우리는 프라미스 객체를 만드는 constructor를 이용할 수 있다. 프라미스는 두 개의 인자를 가진다.

한 개는 성공했을때 (resolved), 그리고 한 개는 (rejected) 됐을떄.

 

 

 

 function isPositive (number, resolve, reject) {
 setTimeout(() => {
	if (typeof number === 'number') {
	 // 성공 -> resolve

	resolve(number>=0? "양수":"음수") 
}
   else { // 실패 -> reject 
	reject("주어진 값이 숫자형 값이 아닙니다");  
 }
  ), 2000)

}

function isPositiveP(number) {
	const exector = (resolve, reject) => { //실행자
 setTimeout (() => {
		if (typeof number === 'number') {
			 // 성공 -> resolve
		  console.log(number);
		
			resolve(number>=0? "양수":"음수") 
		}
	   else { // 실패 -> reject 
		reject("주어진 값이 숫자형 값이 아닙니다");  
	 }
	}, 2000);
};

const asyncTask = new Promise (executor);
return asyncTask;

}

const res = isPositiveP(101);

.then((res) => {
console.log("작업성공 :" ,res)

})
.catch(err) => {
  console.log("작업실패 :", err);

});



isPositive(10, (res) =>{
console.log("성공적으로 수행됨 :", res); 
}, (err)=>{
console.log("실패하였음:" ", err)
});
+ then 체이닝 : 프라미스를 이용하면 코드를 아래로 늘여쓸 수 있다. 콜백지옥에서 탈출가능 가독성이 훨씬 좋음

 


3. async & await

:  기본적으로 프라미스의 설탕 문법이다. 이는 당신의 비동기적 코드를 더욱 동기적이고 절차 지향적인 코드로 보이게 해줄 것입니다. 이로써 사람들이 이해하기 더욱 쉽게 만들어 줌

 

*절차지향적 코드 : 물이 위에서 아래로 흐르는 것처럼 순차적인 처리가 중요시 되며 프로그램 전체가 유기적으로 연결되도록 만드는 프로그래밍 기법

 

Async와 Await의 문법 

 

 

 

async function printMyAsync(){
  await printString("one")
  await printString("two")
  await printString("three")
}

 

신은 껍데기 함수 (Wrapper function)을 위한 “async” 키워드를 사용할 수 있는 것을 볼 수 있다. 이는 자바스크립트가 우리가 async/await를 이용하는 중이라는 것을 알게 해주며, 만약 당신이 Await를 사용하기 원한다면 필수적이다.

이 말은 전역 레벨에서 Await를 사용하지 못한다는 의미는 아니다. 이는 항상 껍데기 함수를 필요로 하거나 혹은 우리는 당신이 await를 이용할 때 그것은 async 함수랑만 이용 된다고 이야기할 수 있다.

await 키워드는 동시 통합된 비동기적 함수안에서 모든 프라미스를 리턴해주는 것을 확실하게 해주는 비동기적 함수에서 쓰인다.

Await는 .then()과 .catch() 메서드안에 콜백의 사용을 제거한다 .

async는 async와 await 사용시 프라미스를 반환 할 때 비동기 앞에 추가된다.

await는 프라미스를 부르기전에 앞에 추가된다.

try .. catch문을 실행할 코드블럭을 표시하고 예외가 발생할 경우 응답을 지정하는데 이것은 비동기적 함수의 값이 거절되었을때 또한 이용된다.

 

 

결론 

우리는 콜백, 프라미스, Async/Await의 개념을 이해하고. 또한 자바스크립트의 비동기적 오청이 어떻게 작동하는 지를 알아보았다.
그리고 이것들은 주로 API 요청과 이벤트를 핸들링할 때 쓰일 것이다.

 

 

Comments