다른 함수가 실행을 끝낸 뒤 실행(call back)되는 함수(⇒ 나중에 호출되는 함수)를 말한다.
다시 말해 코드를 통해 명시적으로 호출하는 함수가 아니라, 함수를 등록해놓은 후 어떤 이벤트가 발생했거나 특정 시점에 도달했을 때 시스템에서 호출하는 함수를 말한다.
파라미터로 함수를 전달받아, 함수의 내부에서 실행된다.
콜백함수 (Callback Function) 사용 이유
자바스크립트에서 비동기적 프로그래밍을 할 수 있기 때문이다.
자바스크립트는 싱글스레드를 사용하는데, 멈춤을 방지해준다. 즉 블록킹을 방지하여 싱글스레드가 논블록킹으로 동작하게 한다.
💡 싱글스레드 ⇒ 싱글스레드는 한 번에 하나의 작업만 수행할 수 있다.
💡 싱글스레드로 어떻게 비동기 작업이 가능할까?
자바스크립트의 메인스레드인 이벤트 루프가 싱글스레드이기 때문에 자바스크립트를 싱글스레드 언어라고 부른다. 그러나 자바스크립트는 이벤트 루프만 독립적으로 실행하지는 않고, 웹 브라우저나 NodeJS 같은 멀티 쓰레드 환경에서 실행된다.
즉, 동시성을 보장하는 비동기, 논블록킹 작업들은 JavaScript 엔진을 구동하는 웹 브라우저, NodeJS (런타임 환경) 에서 담당한다. ⇒ JavaScript 동작원리에서 자세하게 알 수 있다.
콜백함수(Callback Function) 사용 유형
익명 함수 사용
콜백함수는 이름 없는 '익명의 함수'를 사용한다. 함수의 내부에서 실행되기 때문에 이름을 붙이지 않아도 된다.
함수의 이름(만) 넘기기
자바스크립트는 null 과 undefined타입을 제외하고 모든 것을 객체로 다룬다. 함수를 변수 또는 다른 함수의 변수처럼 사용할 수 있다. 함수를 콜백함수로 사용할 경우, 함수의 이름만 넘겨주면 된다.
전역변수, 지역변수를 콜백함수의 파라미터로 전달 가능
전역변수(Global Variable) : 함수 외부에서 선언된 변수
지역변수 (Local Variable) : 함수 내부에서 선언된 변수
콜백함수(Callback Function) 주의할 점
this를 사용한 콜백함수
콜백지옥 (Callback Hell)
콜백 지옥(Callback Hell)은 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 감당하기 힘들정도로 깊어지는 현상이다.
주로 이벤트 처리나 서버 통신과 같은 비동기적인 작업을 수행하기 위해 이런 형태가 자주 등장하는데, 가독성이 떨어지면서 코드를 수정하기 어려워진다.
비동기적인 작업을 수행하기 위해 콜백함수를 익명함수로 전달하는 과정에서 생기는 콜백 지옥을
Promise, async/await, Generator 등을 사용해 방지할 수 있다.
Promise
Promise 란?
프로미스는 자바스크립트 비동기 처리에 사용되는 객체이다.
싱글스레드인 자바스크립트에서 비동기 처리를 위해 사용한 Callback 함수의 에러/예외처리의 어려움, 중첩으로 인한 복잡도 증가라는 단점을 해결하기 위해 프로미스 객체를 ES6에서 언어적 차원으로 지원한다.
💡 Promise가 콜백을 대체하는 것은 아니지만, 콜백을 예측 가능한 패턴으로 사용할 수 있게 하며 Promise 없이 콜백만 사용했을 때 예상치 못한 동작을 막아주거나 찾기 힘든 버그를 상당 수 해결해준다.
Promise 만들기
const promise = new Promise((resolve, reject) => {
/*
비동기 작업 성공시 resolve()를 호출하고,
비동기 작업 실패시 reject()를 호출하도록 구현한다.
*/
})
Promise 성공 시 resolve를 호출하고 실패 시 reject를 호출한다.
const promise = new Promise((resolve, reject)=>{
//처리 내용
})
promise.then(
//resolve가 호출되면 then이 실행
)
.catch(
//reject가 호출되면 catch가 실행
)
.finally(
//콜백 작업을 마치고 무조건 실행되는 finally (생략 가능)
)
Promise 다음엔 then()과 catch()를 사용한다.
then()은 생성한 프로미스 객체에서 인수로 전달한 resolve 가 호출되면 실행된다.
catch()는 생성한 프로미스 객체에서 인수로 전달한 resolve 가 호출되면 실행된다.
// new Promise() 메서드를 호출하면 대기 상태가 된다.
new Promise()
// new Promise() 호출 시 콜백 함수를 선언할 수 있고, 인자는 resolve, reject 이다.
new Promise(function(resolve, reject) {
//...
})
Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
// new Promise() 메서드를 호출하면 대기 상태가 된다.
new Promise()
// new Promise() 호출 시 콜백 함수를 선언할 수 있고, 인자는 resolve, reject 이다.
new Promise(function(resolve, reject) {
//...
})
Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태
// 콜백 함수의 인자 reject를 실행하면 실패(Rejected) 상태가 된다.
new Promise(function(resolve, reject) {
reject()
})
// 실패 상태가 되면 실패한 이유 (실패 처리의 결과 값)를 catch()로 받을 수 있다.
function getData() {
return new Promise(function(resolve, reject) {
reject(new Error("Request is failed"))
})
}
getData().then().catch(function(err) {
console.log(err) // 결과 : Error: Request is failed
})
완료 상태인 Fulfilled 와 Rejected 를 합쳐 settled 라고 한다.
Promise 처리의 흐름
Promise의 에러 처리 방법
실제 서비스를 구현하다 보면 네트워크 연결, 서버 문제 등으로 인해 오류가 발생할 수도 있다.
에러 처리 방법은 2가지가 있는데, 모두 프로미스의 reject() 메서드가 호출되어 실패 상태가 된 경우에 실행된다.
then() 의 두 번째 인자로 에러를 처리하는 방법
getData().then(
handleSuccess,
handleError
)
catch() 를 이용하는 방법
getData().then().catch();
⇒ 프로미스의 에러 처리는 가급적 catch()를 이용하는 것이 효율적이다.
async & await
async & await 란?
async & await 는 비동기식 코드를 동기식으로 표현하여 간단하게 나타내는 것 이다.
기존의 비동기 처리 방식인 Callback 함수의 단점을 보완하기 위해 Promise 를 사용했지만, 코드가 장황하다는 단점이 있었다.
이러한 단점을 해결하기 위해 ES2017(ES8)에서 도입된 비동기 처리 방식의 가장 최신 문법이다.
async & await 는 Promise 객체를 반환한다. ( ⇒ then을 사용할 수 있다.)
async function myAsync(){
return 'async';
}
myAsync().then((result) => {
console.log(result); // 결과 : async
})
프로미스를 반환하고 then()으로 받게 되는 인자 result는 바로 return 값을 의미한다.
async & await 기본 문법
async function 함수명() {
await 비동기_처리_메서드_명()
}
함수의 앞에 async 라는 예약어를 붙인다.
함수의 내부 로직 중 HTTP 통신을 하는 비동기 처리 코드 앞에 await 를 붙인다.
⇒ 비동기 처리 메서드가 꼭 프로미스 객체를 반환해야 await 가 의도한 대로 동작한다.
(일반적으로 await 의 대상이 되는 비동기 처리 코드는 Axios 등 프로미스를 반환하는 API 호출 함수이다.)
async 함수
async 는 function 앞에 위치한다.
funciton 앞에 async 를 붙이면 해당 함수는 항상 프로미스를 반환한다.
프로미스가 아닌 값을 반환하더라도 이행 상태의 프로미스(resolved promise)로 값을 감싸 이행된 프로미스가 반환되도록 한다.
⇒ async 가 붙은 함수는 반드시 프로미스를 반환하고, 프로미스가 아닌 것은 프로미스로 감싸 반환한다.
async 함수는 화살표 함수로도 정의가 가능하고, 함수 표현식으로도 정의가 가능하다.
await
await 는 async 함수 안에서만 동작한다.
자바스크립트는 await 키워드를 만나면 프로미스가 처리(settled)될 때까지 기다리고, 결과는 그 이후 반환된다.
// async & await 예제
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
//2초동안 기다리게 하고 사과를 리턴하는 메서드
async function getApple() {
await delay(2000)
return '🍎'
}
//1초동안 기다라게 하고 사과를 리턴하는 메서드
async function getBanana() {
await delay(1000)
return '🍌'
getApple().then(console.log) // 결과 : 🍎
getBanana().then(console.log) // 결과 : 🍌
async & await 예외 처리
async & await 에서 예외를 처리하는 방법은 try.. catch.. 구문을 사용하는 것이다.
catch {} 를 사용하면 된다. (Promise 에서는 .catch()사용)
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve('success')
}, 1000)
})
}
async function loadData() {
try {
const result = await fetchData()
console.log(result)
} catch(e) {
console.log(e)
}
}
loadData()