[자바스크립트] 콜백함수와 Promise
Web/자바스크립트2022. 10. 26. 11:52
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가 보장하는 특징
- callback 함수는 자바스크립트의 Event Loop가 현재 실행 중인 Call Stack을 완료하기 이전에 절대 호출되지 않음
- Promise callback 함수는 Event Queue에 배치되는 엄격한 순서로 호출됨
- 비동기 작업이 성공하거나 실패한 뒤에 .then() 메서드를 이용하여 추가한 경우에도 1번과 똑같이 동작함
- .then()을 여러 번 사용하여 여러 개의 callback 함수를 추가할 수 있음 (Chaining)
- 각각의 callback은 주어진 순서대로 하나하나 실행하게 됨
- Chaining은 Promise의 가장 뛰어난 장점
3-5. 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초 후 최초 프라미스가 이행됩니다. –
(*)
- 이후 첫번째
.then
핸들러가 호출됩니다. –(**)
- 2에서 반환한 값은 다음
.then
핸들러에 전달됩니다. –(***)
- 이런 과정이 계속 이어집니다.
result
가 핸들러 체인을 따라 전달되므로, alert
창엔 1
, 2
, 4
가 순서대로 출력됩니다.
프라미스 체이닝이 가능한 이유는 promise.then
을 호출하면 프라미스가 반환되기 때문입니다. 반환된 프라미스엔 당연히 .then
을 호출할 수 있습니다.
한편 핸들러가 값을 반환할 때엔 이 값이 프라미스의 result
가 됩니다. 따라서 다음 .then
은 이 값을 이용해 호출됩니다.
3-5. Chaining? - 사용 이유
- 비동기 작업을 순차적으로 실행하기 위해 프로미스 체인을 사용한다.
- 프로미스 체인은 코드를 더 효율적으로 짜기 위해서 사용된다.
- 비동기 코드를 아주 간단하게 정리할 수 있다.
- 체이닝을 어떻게 쪼갤지는 개발자의 재량에 달려있다.
- 아까 고양이코드 체이닝 해보기
- 기본 고양이 코드
<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('야옹야옹')
})
- 체이닝 해보기
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('야옹야옹')
})
- 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('야옹야옹')
})
- 강아지 사진도 가져와보기
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 |
댓글()