솔미는 성장중

[JS] 동기/비동기 (콜백 패턴, promise, Async & Await, Resolve, Reject, 에러 핸들링, 반복문에서의 비동기) 본문

JavaScript

[JS] 동기/비동기 (콜백 패턴, promise, Async & Await, Resolve, Reject, 에러 핸들링, 반복문에서의 비동기)

solming 2023. 7. 31. 17:49
728x90

개념

 동기 : 작업을 동시에 수행하거나, 동시에 끝나거나, 끝나는 동시에 시작
비동기 : 시작, 종료가 일치하지 않으며, 끝나는 동시에 시작을 하지 않음

 

콜백 패턴

 

예시) 비동기로 작동하는 a함수를 콜백 함수를 통해 a,b순으로 실행되게 만들기

const a = (callback)=>{
  setTimeout(()=>{
      console.log(1)
      callback()
  },1000)
}

const b = () => console.log(2)
// a(()=>{})
a(()=>{
  b()
})
//1
//2

 

예시) b함수도 a함수처럼 비동기로 작동하게

const a = (callback)=>{
  setTimeout(()=>{
      console.log(1)
      callback()
  },1000)
}

const b = ()=>{
  setTimeout(()=>{
      console.log(2)
  },1000)
}

const c = () => console.log(3)

a(()=>{
  b()
})
c()
//3
//1
//2

예시) a, b 함수가 비동기로 작동할 때 a,b,c 함수 순으로 실행되게 하고 싶다면?

const a = (callback)=>{
  setTimeout(()=>{
      console.log(1)
      callback()
  },1000)
}

const b = (callback)=>{
  setTimeout(()=>{
      console.log(2)
      callback()
  },1000)
}

const c = () => console.log(3)

a(()=>{
  b(()=>{
  c()
  })
})
//1
//2
//3

이런 식으로 개수가 점점 늘어난다면 개미지옥 형태처럼 생기게 된다.

이것이 바로 콜백지옥!

a(()=>{
  b(()=>{
    c(()=>{
      d()
    })
  })
})

 

읽거나 관리하기 불편하기 때문에 대체하기 위해 만들어진 것이 Promise 클래스!


 

Promise 클래스

앞서 콜백 함수를 설명할 때 썼던 예시와 동일한 예시를 사용한다.

예시) a,b함수 순차적으로 작동하게 만들기 (Promise 클래스를 사용해 변형해준 것.)

const a = ()=>{
  return new Promise(resolve =>{ //생성자함수
  	setTimeout(()=>{
      console.log(1)
      resolve()
  	},1000)
  })
}

const b = () => console.log(2)

a().then(()=>{b()}) //then은 resolve매개변수로 들어감
//1
//2

 

 

예시) a,b,c,d함수가 있을 때 비동기 코드를 순차적으로 작동하게 만들기

const a = ()=>{
  return new Promise(resolve =>{ //생성자함수
  	setTimeout(()=>{
      console.log(1)
      resolve()
  	},1000)
  })
}
const b = ()=>{
  return new Promise(resolve =>{ //생성자함수
  	setTimeout(()=>{
      console.log(2)
      resolve()
  	},1000)
  })
}
const c = ()=>{
  return new Promise(resolve =>{ //생성자함수
  	setTimeout(()=>{
      console.log(3)
      resolve()
  	},1000)
  })
}
const d = ()=>console.log(4)

a().then(()=>{ //a의 resolve로 들어간다.
  return b()   //return new Promise를 통해 return
}).then(()=>{  //b의 resolve로 들어간다.
  return c()
}).then(()=>{
  d()
})
//1,2,3,4

//아래 처럼 정리도 가능. 1
a()
  .then(()=>b())
  .then(()=>c())
  .then(()=>{
   d()
  })
//1,2,3,4
  
//아래 처럼 정리도 가능. 2
a()
  .then(b)
  .then(c) //c라는 함수가 b함수 resolve매개변수 자리로 들어가 콘솔2 출력, resolve실행.
  .then(d)
  .then(()=>console.log('done')
//1,2,3,4,done

 

다른 예시)

겨울왕국-어벤져스-아바타 순으로 출력하는 2가지 방법 (좌: 콜백 패턴, 우: Promise 클래스 사용)


Async & Await

 

예시) await함수를 써서 a,b함수가 순차적으로 작동하게 만들기

const a = ()=>{
  return new Promise(resolve =>{ //생성자함수
  	setTimeout(()=>{
      console.log(1)
      resolve()
  	},1000)
  })
}
const b = () => console.log(2)

const wrap = async {  // await키워드는 async키워드가 붙은 함수로 감싸야 한다.
  await a() // 비동기코드로 만들어진 a함수에서 resolve()까지 실행할 때까지 기다렸다가 
  b()		  // b 호출
}
wrap()
//1
//2
  • await는 Promise instance가 반환되어야만 사용할 수 있다.
  • await키워드는 async키워드가 붙은 함수로 감싸야 한다.

 

다른 예시)

await 키워드를 사용해서 영화 이름이 순서대로 출력되게 하는 방법


Promise 생성자 콜백에서 사용할 수 있는 

Resolve, Reject, 에러 핸들링

 

예제코드1) 

Promise를 사용해서 resolve 상황, reject 상황 코드 구현하기

const delayAdd = index => {
  return new Promise((resolve, reject) => {
    setTimeout(()=>{
      if(index>10){  //10보다 큰 숫자가 들어오면 에러 실행
        reject('${index}는 10보다 커서 안된다.')
        return //reject가 실행되면 resolve는 실행되지 않지만 console.log는 실행될 수 있기에 return
      }
      console.log(index) 
      resolve(index+1) //정상 작동하면 여기 실행
    },1000)
  })
}
//1번째 방법: then 키워드 사용하기
delayAdd(12)
  .then(res=>console.log(res)) 
  .catch(err=>console.error(err)) //catch메소드를 사용하면 reject 매개변수로 들어감
  //12는 10보다 커서 안된다.
  
//2번째 방법: await 사용하기
  const wrap = async () => {
  	try{
      const res = await delayAdd(12) //10보다 큰 수를 넣어서 에러 발생
      console.log(res)	//try catch 구문에서는 에러가 발생하면 그 코드를 제외하고 나머지는 동작 X
    } catch (err) {
      console.error(err)
    } finally {
      console.log('Done!')
    }
  }
  wrap()
  //12는 10보다 커서 안된다.
  //Done!
  • reject가 실행되면 resolve는 실행되지 않지만 console.log는 실행될 수 있기에 return
  • try catch 구문에서는 에러가 발생하면 그 코드를 제외하고 나머지는 동작 X
  • finally를 사용한 부분은 error여부에 관계없이 무조건 실행

예제코드2)

  •  resolve함수가 동작하면 reject와 catch모두 동작하지 않는다.
  • 주소를 잘못 입력해 불러올 수 없게 된다면 '에러 발생 : ~' 내용이 뜨게 된다.
  • catch메소드로 걸러지지 않는 실질적인 에러를 확인하기 위해서 정상 작동을 의미한 then메소드 내에 작성

위 예시 await써서 수정하기)


반복문에서 비동기 처리

 

forEach

- 사용하면 안된다.

- await를 사용해도 순서를 보장하지 않음. 왜냐하면 기다렸다가 다음 콜백으로 넘어가는 게 아니기 때문.

- 콜백이 반복될 때 바로바로 실행된다고 봐야 함.

(아래 사진에서 주석처리된 부분에 해당된다.

 이 부분을 실행하면 frozen, avengers, avatar 순서가 실행할 때 마다 바뀐다.)

 

for 반복문을 사용하자

frozen, avengers, avatar 순으로 출력됨.

 

728x90

'JavaScript' 카테고리의 다른 글

[JS] 불변성  (0) 2023.07.31
[JS] DOM  (0) 2023.07.31
[JS] fetch()  (0) 2023.07.31
[JS] 모듈  (0) 2023.07.23
[JS] prototype + 배열 만드는 방법  (1) 2023.07.19