본문 바로가기
JSP/Muzi

[JavaScript] Promise ,async ,await 개념과 이해 그리고 실습

by 미눅스[멘토] 2024. 7. 4.
728x90

무슨약속? 값이 있으면 값을 반환해주기로 약속

Promise는 jsp 내장 함수

Promise는 비동기 작업의 단위이다.

 

비동기란? 자바스크립트에서 동시에 여러 가지 작업을 할 수 있다는 개념

ㄴex) 심부름꾼을 고용하고 나는 다른일을 함. 그래서 동시에 여러가지 작업을 할 수 있음

 

Promise를 통해 어떻게 비동기 작업을 쉽게 관리할 수 있는지 알아보겠다.

Promise 기본적인 사용법

const myPromise = new Promise((resolve, reject) => {
  // 비동기 작업
});

1. 변수의 이름은myPromise이며, const로 선언했기 때문에 재할당이 되지 않는다. 하나의 변수로 끝까지 해당 Promise를 관리하는 것이 가독성도 좋고 유지보수도 하기 좋다.

 

2.new Promise(...)Promise 객체를 새롭게 만들었다. 생성자 함수와 동일하게 동작하므로, 괄호()를 써서 함수를 호출하는 것과 같은 모습이다.

 

3.생성자는 특별한 함수 하나를 인자로 받는다. (여기서 인자로 들어가는 함수의 형태는 화살표 함수 이다.)

ㄴ 이 특별한 함수를  공식문서에서는 executor라는 이름으로 부른다.. 이 함수에 대한 자세한 설명은 아래와 같다.

 

1.executor는 첫번째 인수로 resolve, 두번째 인수로 reject를 받는다.(변수명은 마음대로 지정해도 상관없다.)

2.resolveexecutor 내에서 호출할 수 있는 또 다른 함수이다. resolve를 호출하게 된다면 "이 함수의 비동기 작업 성공" 이라는 뜻.

3.rejectexecutor 내에서 호출할 수 있는 또 다른 함수 이다. reject를 호출하게 된다면 "이 함수의 비동기 작업 실패" 라는 뜻.

 

말만 화려하지 사실상 별거 없고 async-await 까지 알게 된다면 Promise 에 진면모를 알 수 있게 된다.

 

Promise의 특징으로, new Promise(...) 하는 순간 여기에 할당된 비동기 작업은 바로 시작된다.

 

Promise가 끝나고 난 다음의 동작을 설정해줄 수 있는데 그것은 then 메소드와 catch메소드 이다

then(()=>{}) Promise가 성공했을 때의 동작을 지정한다. 인자로 함수를 받는다.
cath(()=>{}) Promise가 실패했을 때의 동작을 지정한다. 인자로 함수를 받는다.
finally(()=>{}) Promise가 끝나고 무조건 실행할 동작을 지정한다. 인자로 함수를 받는다.

ㄴ 이 함수들은 체인 형태로 활용할 수 있다.(연속적인 호출) 아래서 확인해 보도록 하자.

 

excutor로 새로운 Promise를 만든 다음 then과 catch를 이용하여 후속 동작까지 지정해줘야 어느정도 제대로 돌아가는 Promise를 볼 수 있다.

const myPomise = new Promise((resolve, reject) => {
  resolve();
});

myPomise
  .then(() => {
    console.log("then!");
  })
  .catch(() => {
    console.log("catch!");
  });
  .finally(()=>{
  	console.log("finally!")
  })
  //실제 결과(console)값
  //then!
  //finally!

 

then에 함수를 넣어주고, 연속적으로 catch에도 함수를 넣어줬다. 이 Promise에서는 바로 resolve가 호출되었기 때문에 성공으로 간주하여 then에 있는 동작만 실행 된다. 이제 아래와 같은 코드를 쓴다면 어떻게 될지 생각해보자. resolve 부분을 reject 로 수정했다.

 

const myPromise = new Promise((resolve, reject) => {
  //수정
  reject();
});

myPromise
  .then(() => {
    console.log("then!");
  })
  .catch(() => {
    console.log("catch!");
  });
  .finally(()=>{
  	console.log("finally!")
  })
  
  //실제 결과(console)값
  //catch!
  //finally!

 

 

위 코드들에서는 비동기 작업이라고 해도 뭔가 기다리는 작업은 하나도 하지 않는다. 기다리는 작업이 하나도 없다면 비동기를 쓸 이유가 없다. 위 코드들은 Promise의 동작 방식을 설명하기 위한 코드이다.

 

Promise 재사용 하기

function checkPromise(temp) {
  return new Promise((resolve, reject) => {
    if (temp === true) resolve();
    else reject();
  });
}

const firstMyPromise = checkPromise(true);
firstMyPromise
  .then(() => {
    console.log("첫번째 약속은 사실이야!");
  })
  .catch(() => {
    console.log("첫번째 약속은 거짓이야!");
  })
  //.finally(()=>{
  // console.log("첫번째 약속이 사실이든 거짓이든 난 좋아!")
  //});

const secondMyPromise = checkPromise(false);
secondMyPromise
  .then(() => {
    console.log("두번째 약속은 사실이야!");
  })
  .catch(() => {
    console.log("두번째 약속은 거짓이야!");
  })
  //.finally(()=>{
  //  console.log("두번째 약속이 사실이든 거짓이든 난 좋아!")
  //});

  
 //실제 결과값
 //첫번째 약속은 사실이야!
 //두번째 약속은 거짓이야!

 

checkPromise 함수를 호출하는 순간 new Promise(...)함수가 실행하게 되어 비동기 작업이 시작된다.

그리고 실행을 시켜보면 firstMyPromise는 true를 넣어 then만 실행되고 secondMyPromise는 false를넣어 catch만 실행되는것을 확인할 수 있다. 이제 슬슬 안오던 느낌이 온다!!! 번뜩!!

 

하지만 Promise를 많이 쓰던 개발자들은 다시한번 문뜩 깨닳았다고 한다.. 무엇을??

Promise는 콜백지옥에서 꺼내주는 구세주 인줄 알았지만 또다른.then().catch().finally() 지옥이라는것을....

그래서 나온게 async, await 이고 이 두개는 무조건 세트로 외워버리자!!


async : 함수를 비동기로 만들어주는 놈

앞서 new Promise(...)를 리턴하는 함수를 async 함수로 쉽게 변환 가능하다. 변환 가능하게하는 규칙이 몇가지 있는데 알아보자

 

 

1. async는  함수를 선언할때 함수 앞에 async키워드를 붙여준다.

2.new Promise... 부분을 없애고 executor(화살표 함수) 본문 내용만 남긴다

3.resolve(value); 부분을 return value;로 변경한다.

4.reject(value) 부분을  throw new Error(…);로 수정한다.

 

이제 checkPromise함수를 수정해 보자.

//기존함수
//function checkPromise(temp) {
//  return new Promise((resolve, reject) => {
//    if (temp === true) resolve();
//    else reject();
//  });
//}

//async함수
async function checkPromise(temp) {
    if (temp === true) return "첫번째 약속은 사실이야!";
    else throw new Error(`첫번째 약속은 거짓이야!`)
  });
}


const firstMyPromise = checkPromise(true);
firstMyPromise
  .then((value) => {
    console.log(value);
  })
  .catch((error) => {
    console.log(error);
  })
  //.finally(()=>{
  // console.log("첫번째 약속이 사실이든 거짓이든 난 좋아!")
  //});
  
  //실제결과값
  //첫번째 약속은 사실이야!

 

똑같이 작동하는것을 볼 수 있는데  여기서 알 수 있는것이

async 함수의 리턴 값은 무조건 Promise이다.

위에 함수에서는 우리가 문자열을 리턴했는데, firstMyPromise는 문자열이 아니고 Promise로 온다.

그럼 나는 async함수를 실행시킨 뒤에 then과 catch를 활용해서 흐름을 제어해야만 하나요....??ㅜㅜ 결론은 아니다

async 함수 안에서는 await키워드를 사용가능하다!!! await함수에 대해 알아보도록 하자.

 


await : Promise가 끝날 때까지 기다리라는 뜻

await은 Promise가 끝날때까지 기다리라는 뜻이다.  Promise가 resolve가 되든 reject가 되든 결과가 끝날때 까지 기다리라는 함수이다. 그리고 이  await은 사용가능한 제약 조건이 있는데 알아보자.

 

await함수의 사용 가능한 제약조건

1. async함수 내에서만 사용가능하다.

 

먼저 아래 코드를 돌려보면

const myPromise = async (temp) => {
    setTimeout(()=>{ //비동기 작업
            alert("나야 비동기")
        },1000)
    if(temp){
        return "성공";
    }else{
        throw new Error(`실패`)
    }
}
const myTest = myPromise(true);
myTest.then((result)=>{
    alert("성공",result)
}).catch((result)=>{
    alert("실패",result)
}).finally((result)=>{
    alert("난 무조건")
})

//실제결과 순서
//성공
//난 무조건
//나야 비동기

 

실행순서가 setTimeout()함수가 비동기로 실행되기 때문에 1초동안 다른작업들이 먼저 우선시 되는것을 확인할 수 있다

그럼 이제 awiat 키워드를 이용해 아래 코드를 돌려보자

 

function asyncPromiseSetTimout(){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve();
            alert("나야 비동기")
        },1000)
    })
}
const myPromise = async (temp) => {
    await asyncPromiseSetTimout();
    if(temp){
        return "성공";
    }else{
        throw new Error(`실패`)
    }
}
const myTest = myPromise(true);
myTest.then((result)=>{
    alert("성공",result)
}).catch((result)=>{
    alert("실패",result)
}).finally((result)=>{
    alert("난 무조건")
})

//실제 결과값
//나야 비동기
//성공
//난 무조건

 

 

이렇게 결과를 돌려보면 비동기를 동기처럼 사용가능하다!! 아주 좋군!!

여기까지  Promise, async ,await 에 대해 배워봤는데 여기서 비동기 대표주자 AJAXPromise로 감싸보자

우리는 이것을 멋진말로 Promisefy 라고 한다!!

 


Controller

/*ajaxData*/
@ResponseBody
@RequestMapping(value="/url", method=RequestMethod.POST)
public List<ClassVO> selectAjaxData(@RequestBody ClassVO classVO) throws Exception {
		
	List<ClassVO> classVOList = new ArrayList<ClassVO>(); 
        classVOList = ClassService.dbConnection(classVO.getData())
	return ajaxList;
}

 

JSP : Promisefy

// AJAX 프로미스파이(프로미스화)함수
const ajaxPromise = () => {
    return new Promise((res, rej) => {
        let xhr = new XMLHttpRequest();
        xhr.open("POST", "/url.do", true);  
        xhr.setRequestHeader("content-type", "application/json");//보낼데이터를 json으로 보낼때 
        //xhr.open("get", "/falseUrl.do", true); //  실패할 URL
        xhr.onreadystatechange = () => {
            if (xhr.readyState == 4 && xhr.status == 200) {
                let recResult = JSON.parse(xhr.responseText); //배열이니까JSON
                res(recResult);//성공하면res에 then해주세요
            }
        }
        // 실패 처리(onloadend이벤트는 마지막에 무조건 발생, status값을 이용 에러처링)
        xhr.onloadend = () => {
            if (xhr.status != 200) {
                let myError = { 
                    status: xhr.status,
                    message: xhr.statusText
                };
                rej(myError); //실패하면 rej에 catch해주세요
            }
        };
        
        xhr.send(JSON.stringify({data:"data"})); //보낼데이터
    })
}

//비동기 완료될때까지 기다려!!!
const ajaxData = async () => {
    let rslt = await ajaxPromise();
    alert("나는 ajaxData")
    console.log(rslt)
}

ajaxData(); 
alert("누가먼저 alert가 뜰까요??")

//3초마다 데이터 새로 가져옴
//setInterval(()=>{
//	ajaxData(); 
//},1000*3)

 

함수 안에서 await를 써서 비동기를 동기처럼 사용하였다,

이러면 비동기가 아니지 않나? 질문이 나오는순가 alert 숭서 결과를 생각하라고 e7e샘이 그랬다.

함수자체가 비동기로 동작한다

한번으로는 이해가 잘 안될 수 도 있는데 여러번 쓰다보면 내것이 되겠지...

 

new Promise 생성자 안의 자바스크립트 ajax부분을 jQuery $.ajax로 

바꾸는 연습을 해보는것도 추천한다고 e7e샘이 그랬다.

그래서 한번 바꿔봄

SO~ EZZZZZZZZZZZ

const ajaxPromise = (data) => {
    return new Promise((res, rej) => {
		$.ajax({
			url : "/url",
			contentType : "application/json; charset=utf8",
			data : JSON.stringify(data),
			type : 'POST',
			dataType : "json",
			success:function(result){
				res(result)
			},
		    error: function(xhr, status, error) {
		    	rej(error)
	        }
		})
    })
}

const ajaxData = async (data) =>{
	let rslt = await ajaxPromise(data);
	console.log(rslt)
}