[자바스크립트] 콜백함수와 Promise

Web/자바스크립트|2022. 10. 26. 11:52

Callback과 Promise

Callback과 Promise

1. 비동기 처리의 단점

  • 비동기 처리의 핵심은 Web API로 들어오는 순서가 아니라 작업이 완료되는 순서에 따라 처리한다는 것!
  • 그런데 이는 개발자 입장에서 코드의 실행 순서가 불명확하다는 단점이 있음 이와 같은 단점을 실행 결과를 예상하면서 코드를 작성할 수 없게 함
    • 어떻게 해야 할까?
    • 콜백 함수를 사용하자!

2. 콜백 함수

2-1. 콜백 함수란?

  • 특별한 함수가 아님! 다른 함수의 인자로 전달되는 함수를 콜백 함수라고 한다.
  • 비동기에서만 사용되는 함수가 아니며 동기, 비동기 상관없이 사용 가능하다.
  • 시간이 걸리는 비동기 작업이 완료된 후 실행할 작업을 명시하는 데 사용되는 콜백 함수를 비동기 콜백이라 부른다.

2-2. 콜백 함수 예시

const btn = document.querySelector('button')
btn.addEventListener('click' () => {
	alert('completed')
})
  • 자바스크립트의 이벤트리스너

2-3. 콜백 함수를 사용하는 이유

  • 명시적인 호출이 아닌 특정한 조건 혹은 행동에 의해 호출되도록 작성할 수 있음
  • ‘요청이 들어오면’, ‘이벤트가 발생하면’, ‘데이터를 받아오면’ 등의 조건으로 이후 로직을 제어할 수 있음
  • 비동기 처리를 순차적으로 동작할 수 있게 함
  • 비동기 처리를 위해서는 콜백 함수의 형태가 반드시 필요하다.

2-4. 콜백 지옥

  • 콜백 함수는 연쇄적으로 발생하는 비동기 작업을 순차적으로 동작할 수 있게 함
  • 보통 어떤 기능의 실행 결과를 받아서 다른 기능을 수행하기 위해 많이 사용하는데, 이 과정을 작성하다 보면 비슷한 패턴이 계속 발생하게 됨
  • 콜백 지옥 예시

2-5. 정리

  • 콜백 함수는 비동기 작업을 순차적으로 실행할 수 있게 하는데 반드시 필요한 로직
  • 비동기 코드를 작성하다 보면 콜백 함수로 인한 콜백 지옥은 반드시 나타나는 문제
    • 코드의 가독성을 해치고
    • 유지 보수가 어려워진다

3. 프로미스

3-1. 프로미스란?

  • 콜백 지옥 문제를 해결하기 위해 등장한 비동기 처리를 위한 객체순서 보장
  • ‘작업이 끝나면 실행 시켜줄게’ 라는 약속(promise)
  • 비동기 작업의 완료 또는 실패를 나타내는 객체
  • Promise 기반의 클라이언트가 바로 이전에 사용한 Axios 라이브러리!
    • 성공에 대한 약속 then()
    • 실패에 대한 약속 catch()

3-2. 프로미스(Promise) - then, catch

  • then(callback)
    • 요청한 작업이 성공하면 callback 실행
    • callback은 이전 작업의 성공 결과를 인자로 전달 받음
  • catch(callback)
    • then()이 하나라도 실패하면 callback 실행
    • callback은 이전 작업의 실패 객체를 인자로 전달 받음

3-3. 프로미스(Promise) - chaining

  • then과 catch 모두 항상 promise 객체를 반환 즉, 계속해서 chaining을 할 수 있음
  • axios로 처리한 비동기 로직이 항상 promise 객체를 반환그래서 then을 계속 이어 나가면서 작성할 수 있던 것
  • return
    으로 넘겨줘야 계속 이어나갈 수 있음..!
work1()
	.then((result1) => {  // 성공하면 수행할 1번 콜백 함수
		//work2
		return result2
	})
	.then((result2) => {  // 1번 콜백함수가 성공하면 수행할 2번 콜백 함수
		//work3
		return result3
	})
	.then((result3) => {  // 2번 콜백함수가 성공하면 수행할 3번 콜백 함수
		//work4
		return result4
	})
	.catch((error) => {   // 어떤 then에서든 실패하면 바로 catch로 데이터가 넘어와서 해당 then의 실패 정보를 줌
		// error handling
	})

 

  • 비동기 콜백으로 작성하면..
work1(function () {
	// 첫번째 작업 ...
	work2(result1, function (result2) {
		// 두번째 작업 ...
		work3(result2, function (result3) {
			// ....
		})
	})
})

 

3-4. Promise가 보장하는 것 (vs 비동기 콜백)

  • 비동기 콜백 작성 스타일과 달리 Promise가 보장하는 특징
  1. callback 함수는 자바스크립트의 Event Loop가 현재 실행 중인 Call Stack을 완료하기 이전에 절대 호출되지 않음
    • Promise callback 함수는 Event Queue에 배치되는 엄격한 순서로 호출됨
  1. 비동기 작업이 성공하거나 실패한 뒤에 .then() 메서드를 이용하여 추가한 경우에도 1번과 똑같이 동작함
  1. .then()을 여러 번 사용하여 여러 개의 callback 함수를 추가할 수 있음 (Chaining)
    • 각각의 callback은 주어진 순서대로 하나하나 실행하게 됨
    • Chaining은 Promise의 가장 뛰어난 장점

3-5. Chaining? -

프라미스 체이닝
콜백 챕터에서 언급한 문제를 다시 집어봅시다. 스크립트를 불러오는 것과 같이 순차적으로 처리해야 하는 비동기 작업이 여러 개 있다고 가정해 봅시다. 어떻게 해야 이런 상황을 코드로 풀어낼 수 있을까요? 프라미스를 사용하면 여러 가지 해결책을 만들 수 있습니다. 이번 챕터에선 프라미스 체이닝(promise chaining)을 이용한 비동기 처리에 대해 다루도록 하겠습니다. 프라미스 체이닝은 아래와 같이 생겼습니다.
https://ko.javascript.info/promise-chaining
new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000); // (*)

}).then(function(result) { // (**)

  alert(result); // 1
  return result * 2;

}).then(function(result) { // (***)

  alert(result); // 2
  return result * 2;

}).then(function(result) {

  alert(result); // 4
  return result * 2;

});

프라미스 체이닝은 result가 .then 핸들러의 체인(사슬)을 통해 전달된다는 점에서 착안한 아이디어입니다.

위 예시는 아래와 같은 순서로 실행됩니다.

  1. 1초 후 최초 프라미스가 이행됩니다. – (*)
  1. 이후 첫번째 .then 핸들러가 호출됩니다. –(**)
  1. 2에서 반환한 값은 다음 .then 핸들러에 전달됩니다. – (***)
  1. 이런 과정이 계속 이어집니다.

result가 핸들러 체인을 따라 전달되므로, alert 창엔 124가 순서대로 출력됩니다.

프라미스 체이닝이 가능한 이유는 promise.then을 호출하면 프라미스가 반환되기 때문입니다. 반환된 프라미스엔 당연히 .then을 호출할 수 있습니다.

한편 핸들러가 값을 반환할 때엔 이 값이 프라미스의 result가 됩니다. 따라서 다음 .then은 이 값을 이용해 호출됩니다.

3-5. Chaining? - 사용 이유

  1. 비동기 작업을 순차적으로 실행하기 위해 프로미스 체인을 사용한다.
  1. 프로미스 체인은 코드를 더 효율적으로 짜기 위해서 사용된다.
  1. 비동기 코드를 아주 간단하게 정리할 수 있다.
  1. 체이닝을 어떻게 쪼갤지는 개발자의 재량에 달려있다.

 

  • 아까 고양이코드 체이닝 해보기
  1. 기본 고양이 코드
<button>야옹아 이리온</button>

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

console.log('고양이는 야옹')
const catImageSearchURL = 'https://api.thecatapi.com/v1/images/search'
const btn = document.querySelector('button')

btn.addEventListener('click', function () {
  axios.get(catImageSearchURL)
    .then((response) => {
      imgElem = document.createElement('img')
      imgElem.setAttribute('src', response.data[0].url)
      document.body.appendChild(imgElem)
    })
    .catch((error) => { 
      console.log('실패했다옹')
    })
    console.log('야옹야옹') 
})
  1. 체이닝 해보기
btn.addEventListener('click', function () {
  axios.get(catImageSearchURL)
    .then((response) => {
      imgElem = document.createElement('img')
      return response
    })
    .then((response) => {
      imgElem.setAttribute('src', response.data[0].url)
      document.body.appendChild(imgElem)
    })
    .catch((error) => { 
      console.log('실패했다옹')
    })
    console.log('야옹야옹') 
})
  1. axios 권장되는 방식으로 작성해놓기
btn.addEventListener('click', function () {
  axios({
    method : 'get',
    url : catImageSearchURL,
  })
    .then((response) => {
      imgElem = document.createElement('img')
      return response
    })
    .then((response) => {
      imgElem.setAttribute('src', response.data[0].url)
      document.body.appendChild(imgElem)
    })
    .catch((error) => { 
      console.log('실패했다옹')
    })
    console.log('야옹야옹') 
  })
  1. 강아지 사진도 가져와보기
const dogImageSearchURL = 'https://dog.ceo/api/breeds/image/random'
const dogBtn = document.querySelector('#dog-btn')

dogBtn.addEventListener('click', function (event) {
  axios({
    method: 'get',
    url : dogImageSearchURL,
  })
    .then((response) => {
      imgElem = document.createElement('img')
      console.log(response)
      const imgSrc = response.data.message
      return imgSrc
    })
    .then((response) => {
      imgElem.setAttribute('src', response)
      document.body.appendChild(imgElem)
    })
    .catch((error) => { 
      console.log('실패했멍')
    })
    console.log('멍멍') 
})

 

'Web > 자바스크립트' 카테고리의 다른 글

[자바스크립트] Axios 라이브러리  (0) 2022.10.26
[자바스크립트] 동기, 비동기  (0) 2022.10.26
[자바스크립트] this  (0) 2022.10.24
[자바스크립트] event  (0) 2022.10.24
[자바스크립트] DOM  (0) 2022.10.24

댓글()