본문 바로가기

개념/javaScript

[javaScript] 비동기, Promise,async/await

동기(synchronous) vs 비동기(asynchronous)

  동기(synchronous) 비동기(asynchronous)
예시 1.손님1이 아메리카노 주문 (손님2 기다리는중)
2.아메리카노 완성 및 손님1에게 전달(손님2 기다리는중)
3.손님2가 카페라떼 주문
4.손님 카페라떼 완성 및 손님2에게 전달
1.손님1이 아메리카노 주문
    1-1.아메리카노 만들기 (손님2 주문 받는 중)
    1-2.아메리카노 완성되면 손님에게 전달
2.손님2가 카페라떼 주문(손님1 아메리카노 만드는 중)
    2-1.카페라떼 만들기 
    2-2.카페라떼  완성되면 손님에게 전달
특징 어떤 일을 수행하는 동안 다른 것은 수행 못한다.(blocking)
비유하자면 전화같은 것
어떤 일을 수행하면서 다른 일을 수행할 수 있다.(non-blocking)
비유하자면문자같은 것
정의 요청에 대한 결과가 동시에 일어난다. 요청에 대한 결과가 동시에 일어나지 않는다.
사례   -DOM Element의 이벤트 핸들러
-타이머(setTimeout, requestAnimationFrame등)
-서버의 자원 요청 및 응답(fetchAPI,AJAX(XHR))
    이벤트 루프 참고링크1
이벤트 루프 참고링크2

 


비동기 함수 전달 패턴

비동기 함수에서는 동기 함수처럼 한 함수들이 순차적으로 수행되는 것이 아니고,

각각의 함수 수행에 걸리는 시간도 다 다르기 때문에 뭐가 먼저 끝날지 알 수가 없다.

 

그런데, 비동기적으로 실행되더라도 어떤 부분들은 

뭔가가 수행되고 난 다음에 수행되야만 하는 경우도 있다.

이때는 따로 지정해줘야한다.

그런 것들을 지정하는 방식, 패턴에는 아래의 두가지가 있다.

 

1.callback패턴

let request = 'caffeLatte';
orderCoggeeAsync(request,function(response){
	drink(response);
})

2.이벤트 등록 패턴

let request = 'caffelatte';
orderCoffeeAsync(request).onready = function(response){
	drink(response)
}

콜백의 예

반복실행하는 함수(iterator)

[1,2,3].map(function(element, index){
	return element * element
})

이벤트에 따른 함수(event handler)

document.querySeletor('#btn').addEventListener('click',function(e){
	console.log('button clicked')
})

콜백에서 주의

함수의 실행을 연결❌

함수 자체를 연결⭕️

function handleClick(){
	console.log('button clicked')
}

//⭕️
document.querySelector('#btn').onclick = handleClick;
//⭕️
document.querySelector('#btn').onclick = function(){
	handleClick();
}
//⭕️
document.querySelector('#btn').onclick = handleClick.bind();

//❌
document.querySelector('#btn').onclick = handleClick();

콜백 함수 어떻게 하면 더 효율적으로 부를까

 

1. callback HELL : 함수안에 함수안에 또 함수안에 함수 넣기

이렇게 하면 너무 코드가 장황하고 이해하기도 힘들다

const printString = (string,callback) => {
	setTimeout(
    	() => {
        	console.log(string)
            callback()
        },
        Math.floor(Math.random()*100) + 1
    )
}

const printAll = () => {
	printString("A",()=>{
    //함수 안에 콜백함수
    	printString("B",()=>{
        //또 그 콜백함수의 콜백함수
        	printString("C",()=>{
            //또또 그 콜백함수의 콜백함수
            })
        })
    })
}
printAll()

 

2. Promise : 콜백 체인을 다루기 위한 방법

promise비동기 작업의 최종 완료 또는 실패를 나타내는 객체이다.

promise에 대한 내용은 아래의 더보기 버튼 클릭!

👇🏻👇🏻👇🏻

더보기

아래의 내용은 유튜브 "코딩앙마"님의 <자바스크립트 중급 강좌 #16 프로미스(Promise)>를 보고,

생각안날때 보려고 만든 자료입니다!

const pr = new Promise((resolve,reject)=>{
  //code
})
//resolve와 reject는 모두 콜백 함수이며,
//resolve는 성공했을 때,
//reject는 실패했을 때 실행된다.
이미지 캡처 :&nbsp;https://youtu.be/3Ao3OroqQLQ

위 도식을 코드로 살펴보면 아래와 같다.

resolve와 reject의 상황을 각각 만들어보면 다음과 같다.

이미지 캡처 :&nbsp;https://youtu.be/3Ao3OroqQLQ
이미지 캡처 :&nbsp;https://youtu.be/3Ao3OroqQLQ
이미지 캡처 :&nbsp;https://youtu.be/3Ao3OroqQLQ

이제 소비자의 입장에서 보면 pr.then을 사용해 pr이란 prmise객체를 거쳐서 나온 결과값이 성공인지 실패인지에 따라 then으로 반응할 수 있다. 이때 then안의 콜백두개는 각각 성공시와 실패시의 콜백이며, 안에 들어가는 인자는 pr에서 나온 값이 된다.

위 경우에선 resolve되는 경우이므로 OK가 result로 들어가게 된다. 

 

.then(resolve시콜백, reject시 콜백) => .then(function(result){}, function(err){}) 이런식으로 적는 방법보다 가독성 좋게 적는 방법이 있는데, 그게바로 catch이다.

이미지 캡처 :&nbsp;https://youtu.be/3Ao3OroqQLQ

.then(resolve시콜백).catch(reject시 콜백) => .then(function(result){}).catch(function(err){}) 

 성공시(resolve) 콜백을 then안에 적고, 연결시켜서 실패의 경우(reject)튕겨져나간 실패의 결과값을 catch로 잡아준다.

한눈에 어디가 성공코드고, 어디가 실패코드인지 알 수 있어 보기 좋다.

 

then과  catch말고 쓸 수 있는게 또 있는데 바로 finally이다.

finally는 이행이든 거부든 처리가 완료되면 항상 실행된다. 따라서 로딩화면 같은 것을 없앨때 유용하게 쓸 수 있다.

이미지 캡처 :&nbsp;https://youtu.be/3Ao3OroqQLQ

 직접 코드를 작성하며 테스트해보면 이해가 빠르다.

const pr = new Promise((resolve,reject)=>{
  setTimeout(()=>{
    // resolve('OK')
    reject(new Error('error....'))
  },1000)
})

console.log('시작')
pr.then(result => {
  console.log(result)
}).catch(err => {
  console.log(err)
}).finally(()=>{
  console.log("끝")
})

👆🏻👆🏻👆🏻

 

 

const printString = (string) => {
  //함수 자체를 받을 떄 callback함수를 받을 필요가 없다.
  //대신 새로운 Promise객체를 만들어준다.
  return new Promise((resolve,reject)=>{
    //이 Promise는 자신만의 (resolve와 reject을 인자로 받는) 콜백을 받는다.
    //resolve === 함수
    //에러 핸들링까지 해주려면 reject사용
    setTimeout(
    	() => {
        	console.log(string)
          resolve()
        },
        Math.floor(Math.random()*100) + 1
    )
  })
}

const printAll = () => {
 printString("A")
  .then(()=>{
    return printString("B")
  })
  .then(()=>{
    return printString("C")
  })
  //reject가 작성된경우 .catch사용가능
}
printAll()

 

이미지 출처:&nbsp;https://kevinyckim33.medium.com/what-are-promises-in-javascript-f1a5fc5b34bf

 

이 promise를 활용한 방법에서 return 처리를 제대로 해주지 않으면(promise chainning을 적절히 사용하지 않으면) 아래와 같이 promise hell에 빠질 수 있다.

promise hell 상황을 보려면 아래의 더보기 클릭!

👇🏻👇🏻👇🏻

더보기
function gotoWork(){
  return new Promise((resolve,reject) => {
    setTimeout(()=>{ resolve("1. I go to Work") },100)
  })
}

function sitAndWork(){
  return new Promise((resolve,reject) => {
    setTimeout(()=>{ resolve("2. I sit and work") },100)
  })
}

function goHome(){
  return new Promise((resolve,reject) => {
    setTimeout(()=>{ resolve("3. I go Home") },100)
  })
}

function getSleep(){
  return new Promise((resolve,reject) => {
    setTimeout(()=>{ resolve("4. I get sleep") },100)
  })
}

gotoWork()
.then(data => {
  console.log(data)
  
  sitAndWork()
  .then(data => {
    console.log(data)
    
    goHome()
    .then(data => {
      console.log(data)
      
      getSleep()
      .then(data => {
        console.log(data)
      })
    })
  })
})

//'1. I go to Work'
//'2. I sit and work'
//'3. I go Home'
//'4. I get sleep'

👆🏻👆🏻👆🏻

 

3. async/await : 콜백 체인을 다루기 위한 또다른 방법

promise의 syntactic sugar이다.

async/await를 더 알아보려면 클릭!

👇🏻👇🏻👇🏻

더보기

아래의 내용은 유튜브 "코딩앙마"님의 <자바스크립트 중급 강좌 #17 async,await>를 보고,

생각안날때 보려고 만든 자료입니다!

 

async라는 키워드를 앞에 두고 함수를 선언하면 결과 값이 Promise객체가 된다.

async function getName(){
  return "Mike";
}

console.log(getName()) //Promise { 'Mike' }

때문에 아래와 같이 await로 연결이 가능하다.

async function getName(){
  return "Mike";
}

getName().then((name)=>{
	console.log(name)
})
//"Mike"
async function getName(){
  return Promise.resolve('Tom')
}
getName().then((name)=>{
	console.log(name)
}) //'Tom'

 

await 키워드는 async함수 내부에서만 사용할 수 있다. 일반함수에서는 사용불가

function getName(name){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
  		resolve(name);
    }, 1000)
  })
}

async function showName(){
  const result = await getName('Mike')
  //변수에 데이터가 await되었다가 들어간다는게 명확하게 보임
  console.log(result)
}

console.log("시작") //시작
showName() //1초 후에 Mike찍힘

 

이번에는 promise코드를 async/await로 바꿔본다.

먼저 promise버전

const f2 = (message) => {
  console.log(message)
  return new Promise((res,rej) => {
    setTimeout(()=>{
      res("2번 주문 완료")
    }, 3000)
  })
}

const f3 = (message) => {
  console.log(message)
  return new Promise((res,rej) => {
    setTimeout(()=>{
      res("3번 주문 완료")
    }, 2000)
  })
}

f1()
	.then((res) => f2(res))
  .then((res) => f3(res))
  .then((res) => console.log(res))
  .catch(console.log)

 

그리고 같은 기능을 구현하는 async/await 버전

뭔가 순서가 한눈에 더 알기 쉽다.

const f1 = () => {
  return new Promise((res,rej) => {
    setTimeout(()=>{
      res("1번 주문 완료")
    }, 1000)
  })
}

const f2 = (message) => {
  console.log(message)
  return new Promise((res,rej) => {
    setTimeout(()=>{
      res("2번 주문 완료")
    }, 3000)
  })
}

const f3 = (message) => {
  console.log(message)
  return new Promise((res,rej) => {
    setTimeout(()=>{
      res("3번 주문 완료")
    }, 2000)
  })
}

// f1()
// 	.then((res) => f2(res))
//   .then((res) => f3(res))
//   .then((res) => console.log(res))
//   .catch(console.log)
console.log('시작')
async function order(){
  const result1 = await f1();
  const result2 = await f2(result1);
  const result3 = await f3(result2);
  console.log(result3)
  console.log('종료')
}
order()

 

async/await문에선 에러가 났을떼(rejected) catch문이 아닌 try catch문으로 감싸준다.

const f1 = () => {
  return new Promise((res,rej) => {
    setTimeout(()=>{
      res("1번 주문 완료")
    }, 1000)
  })
}

const f2 = (message) => {
  console.log(message)
  return new Promise((res,rej) => {
    setTimeout(()=>{
      // res("2번 주문 완료")
      rej(new Error("error.."))
    }, 3000)
  })
}

const f3 = (message) => {
  console.log(message)
  return new Promise((res,rej) => {
    setTimeout(()=>{
      res("3번 주문 완료")
    }, 2000)
  })
}

console.log('시작')
async function order(){
  try{
    const result1 = await f1();
  	const result2 = await f2(result1);
  	const result3 = await f3(result2);
    console.log(result3)
  }catch(e){
    console.log(e)
    //에러로그 찍고
  }
  //그 다음 작업으로 넘어감
  console.log('종료')
}
order()

👆🏻👆🏻👆🏻

 

 

function gotoWork(){
  return new Promise((resolve,reject) => {
    setTimeout(()=>{ resolve("1. I go to Work") },100)
  })
}

function sitAndWork(){
  return new Promise((resolve,reject) => {
    setTimeout(()=>{ resolve("2. I sit and work") },100)
  })
}

function goHome(){
  return new Promise((resolve,reject) => {
    setTimeout(()=>{ resolve("3. I go Home") },100)
  })
}

function getSleep(){
  return new Promise((resolve,reject) => {
    setTimeout(()=>{ resolve("4. I get sleep") },100)
  })
}

const result = async () => {
  //앞에 async함수라고 표시를 해주고
  //await을 사용해서 비동기 함수를 마치 동기함수처럼 쓴다.
  const one = await gotoWork();
  console.log(one)
  
  const two = await sitAndWork();
  console.log(two)
  
  const three = await goHome();
  console.log(three)
  
  const four = await getSleep();
  console.log(four)
}

result();

//'1. I go to Work'
//'2. I sit and work'
//'3. I go Home'
//'4. I get sleep'

콜백 에러 핸들링

const somethingGonnaHappen = callback =>{
  waitingUntilSomethingHappens()
  
  if(isSomethingGood){
    callback(null,something)
  }
  
  if(isSomethingBad){
    callback(something,null)
  }
}

somethingGonnaHappen((err, data) => {
  if(err){
    console.log('ERR!!');
    return;
  }
  return data;
})

 

'개념 > javaScript' 카테고리의 다른 글

[javaScript] async/await  (0) 2022.08.01
[javaScript] Promise란? + 관련 메서드들  (0) 2022.07.29
[javaScript] 프로토타입 체인  (0) 2022.07.25
[javaScript] 정규 표현식  (0) 2022.07.24
[javaScript] 프로토타입  (0) 2022.07.22