javascript 의 비동기 관해 배울때 callback -> Promise -> async await
순서로 알려주는 로드가 제법 많은데요?
주위 JS 개발자 주니어들을 보면, 오히려 async await 을 먼저 학습하고 익숙해지고 나서 Promise를 재대로 이해 하기 시작하는 케이스를 제법 많이 보게 됩니다.
글쓴이도 java기반 백앤드 base 이기때문에 js 의 비동기,callback 라는걸 다루고 이해하기 힘들엇었음
아직 비동기통신 ( data fetch ) 할 때 만 async await 을 사용하셨다면, 제가 Promise 라는게 얼마나 멋있는 Class 인지, async await 을 더 다양하게 사용하는 방법을 전달 하려고 합니다.
해당 글은 Promise 를 최대한 쉽게 설명하기 위해 Promise 의 목적 이해 를 토대로 작성 됐습니다.
Promise 를 재대로 이해 하기전에, 우리가 통신을 하기 위해 굉장히 많이 사용한 키워드
async await
키워드는 어떤 것에 사용 할 수 있을까요?
Promise Class 에만 사용 할 수 있습니다. 아니, Promise 에만 효과를 얻습니다.
예를 들어 보겠습니다.
sum(1,2) 에는 await 을 할 수 있을까요?
const sum = (a,b) => a+b
const testFunction = async ()=>{
await sum()
}
sum 은 동기적인 함수 입니다. await 을 하게되면 별다른 효과가 없을 뿐, 런타임에러를 발생시키진 않습니다.
setTimeout
은 효과가 있을까요?
const testFunction = async ()=>{
console.log(1);
await setTimeout( ()=>{console.log(2)}, 3000 )
console.log(3);
}
실행 결과 출력 순서는 1 3 2 로 될 것 입니다.
await 키워드를 사용했을때 정말로 await(기다릴수있는) 하는 것은
비동기 라기 보단, 정확히 Promise 객채 인 경우 입니다.
결과적으로 우리가 지금까지 사용했던 await
이 붙은 함수들은 전부 Promise 를 retrun 하는 함수 인 것 입니다.
ex : fetch(), axios(), databaseConnected(), async function 등등
또한 전혀 비동기 적인 내용은 없지만 함수에 async 키워드를 사용하는 순간, Promise 를 리턴하는 함수가 됩니다.
const testFunction = async ()=>{
console.log('hello');
}
console.log(testFunction()) // Promise {<fulfilled>: undefined}
Promise,async await 의 목적은 비동기 적 인것을 동기적으로 컨트롤 할수 있게 해주는 것 입니다.
비동기 적인 코드를 동기적으로 컨트롤한다? 🤔
코드는 한줄 한줄 순차적으로 실행 됩니다. 아래 코드는 동기적 (순차적?) 인 코드 입니다.
const testFunction = ()=>{
console.log(1);
console.log(2);
console.log(3);
}
testFunction(); // 1,2,3 순으로 출력 됨.
하지만 아래 코드는 어떨까요?
const testFunction = ()=>{
console.log(1);
document.getElementById('#btn').addEventListeners('click',()=> console.log(2) )
console.log(3);
}
testFunction();
당연히 1에 이어서 3이 출력되고, 언젠간 ? 혹은 누르지 않을 수도 있는 버튼
을 클릭했을때 2가 출력 됩니다.
만약, 위 함수에서 1 2 3 순으로 출력 되기를 기대 한다면 이 함수는 어찌보면, '비동기 적이다' 라고 도 생각 할 수 도 있을 것 같아요.
위 함수를 동기적으로 만들어 줄 수 있는게 Promise 객채 입니다.
const testFunction = async ()=>{
console.log(1);
await 버튼을누를때까지기다리기();
console.log(2);
console.log(3);
}
testFunction();
Promise 는 생성자에 인자로 resolve와 reject 라는 두개의 매개변수 함수를 제공 합니다.
reject 에 대한 설명은 빼고 작성하겠습니다. resolve를 이해했다면 별거 아닌 거니깐요
말이 조금 어려운데요 Promise( 약속 )를 생성할때 resolve( 완료 ) 라는 것을 제공 받는데
resolve 는 약속이 '해결됐다' 라고 약속의 상태를 대기중 -> 완료 로 변경하는 함수 라고 생각하시면 됩니다.
// Promise에 대해 배울때 많이 나오는 예제
const promise = new Promise((resolve)=>{ // new Promise => 약속을 생성함
setTimeout(()=>{
resolve() // 5초 뒤에 이 약속은 대기중인 상태에서 완료 됨 상태로 변경
},5000)
})
즉 어떠한 약속(new Promise)을 생성하고, 내가 원하는 시점에서 '완료'( resolve() ) 라는 상태변경을 직접 컨트롤 할수 있게됩니다.
그리고 await 이라는 키워드를 통해서 Promise(약속) 이 resolve(완료) 될때 까지 기다릴수 있게 됩니다.
const testFunction = async ()=> {
console.log(1);
await new Promise((resolve)=>{ // 약속 생성
document.getElementById('btn').addEventListeder('click',()=>{
console.log(2);
resolve(); // 버튼을 클릭 했을때 약속이 완료됨
},{once:true}) )
})
console.log(3);
}
그리고 resolve(1); 이렇게 약속완료 함수에 값을 넣게 되면 그 값을 뱉게 됩니다.
결국 우리가 자주 사용하던 fetch axios 같은 함수들은 아래 코드 처럼 생겼을 것 이라고 추측 할 수 있습니다.
// 실제는 XMLHttpRequest 라는 객채를 사용합니다. 아래는 추상적인 소스 입니다.
const fetch = (url,options)=>{
const promise = new Promise( resolve =>{ // 약속 생성
const httpRequest = new httpRequest(url,options); // 서버에 요청하는 함수 생성
httpRequest.addEventListeners('onResponse', response=>{
// 응답이 왔을 때 ( 버튼을 클릭했을때 와 비슷하게 표현)
resolve(response); // 약속 완료
})
})
return promise; // 이러한 약속을 리턴
}
const getProducts = async ()=>{
const response = await fetch('https://shop.com/products')
....
}
추천드리고 싶은 것은, 프로젝트에서 공통으로 많이 사용하는 confirm() 입니다.
예를들어 확인 버튼을 눌렀을 때 resolve(true); 를 해주고, 취소 버튼을 눌렀을 때 resolve(false);
를 해주면서 아래 코드 처럼 사용 할 수 있게 만들어 보면 어떨 까요?
const deleteItem = async (id)=>{
const answer = await customConfirm(' 정말 삭제할까요? ');
if ( answer ) fetch(`/delete/${id}`);
...
}