
우선순위 작업이 끝날 때까지 기다리는동안 준비상태가 되기 때문에 다른 작업을 할 수 없다.
특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 자바스크립트의 특성. 동시에 여러가지 작업을 처리할 수 있고 다른 함수를 호출 할 수도 있다.
자바스크립트는 웹에 사용하기 위해 만들어진 언어이다. 웹은 request를 보내거나 사용자의 상호 작용을 기다려야 하는 일이 잦기 때문에 비동기 프로그래밍이 특히 많이 쓰이게 된다. 자바스크립트에서는 Callback, Promise을 사용해 비동기 처리를 구현한다.
다른 함수에 매개변수로 넘겨주는 것
function greet(name, callback) {
callback(`Hello, ${name}!`);
}
function printGreeting(greeting) {
console.log(greeting);
}
greet("Alice", printGreeting); //"Hello, Bob!"
다음과 같이 화살표 함수를 사용하여 표현할 수도 있다.
const greet = (name, callback) => callback(`Hello, ${name}!`);
const printGreeting = greeting => console.log(greeting);
greet("Bob", printGreeting); //"Hello, Bob!"
setTimeout이나 setInterval과 같은 타이머 함수에서 일정 시간마다 스크립트를 실행하는 용도로써 콜백 함수를 이용한다.
// 3000 밀리초(3초) 후에 콜백 함수 실행
setTimeout(function () {
console.log("Time is up!");
}, 3000);
만약 다음과 같은 코드가 있다면 비동기/동기 실행 시 어떻게 될까?
console.log("1");
setTimeout(() => console.log("2"), 3000);
console.log("3");

동기 실행 시에는 setTimeout이 끝난 뒤에 "2"와 "3"이 순차적으로 출력된다. 비동기 실행 시에는 setTimeout으로 타이머가 시작되고 "3"이 출력된 뒤, 3초가 지난 후에 "2"가 출력되고 끝나게 된다. 즉, 비동기이 오래 걸리는 작업이 있을 때, 다른 작업을 먼저 처리해줌으로써 시간을 절약해준다. setTimeout과 같은 함수들을 비동기 함수라고 한다.
비동기 함수의 콜백은, 아무리 빨리 실행돼도 비동기 함수 이후에 있는 코드가 모두 실행된 후에 실행된다. 비동기 함수 이후에 있는 코드가 굉장히 오래 걸려도, 그걸 모두 실행하고 콜백을 실행하게 된다. 하지만 대부분의 경우 비동기 작업이 훨씬 오래 걸리고, 콜백의 실행 타이밍이 몇 밀리초 단위까지 중요한 경우는 별로 없기 때문에 문제가 되는 경우는 거의 없다.
console.log('1');
setTimeout(() => console.log('2'), 1001);
setTimeout(() => console.log('3'), 1000);
console.log('4');
하지만, 다음과 같이 비동기 함수가 여러 개일 경우에는 동기적으로 실행된다.
setTimeout 외에 다른 비동기 함수로는 setInterval() 함수, DOM의 addEventListener() 함수, React의 useEffect() 함수, Express의 get() 함수 등이 있다.

웹 서비스를 개발하다 보면 서버에서 데이터를 받아와 화면에 표시하기까지 인코딩, 사용자 인증 등을 처리해야 하는 경우가 있다. 만약 이 모든 과정을 비동기로 처리해야 한다고 하면 위와 같이 콜백 안에 콜백을 계속 무는 형식으로 코딩을 하게 된다. 이러한 코드 구조는 가독성도 떨어지고 로직을 변경하기도 어렵다. 즉, 함수의 매개 변수로 넘겨지는 콜백 함수가 반복되어 코드의 들여쓰기 수준이 감당하기 힘들 정도로 깊어지는 현상을 콜백 지옥이라고 부른다. 이를 해결하기 위해 Promise 문법이 등장하게 되었다.
비동기 작업이 완료되면 값을 알려주는 객체로, Promise를 돌려주고 나중에 작업이 완료되면 결과값을 Promise에 채워준다.
const myPromise = new Promise((resolve, reject) => {
// 비동기 작업을 수행한다. 여기서는 1초 뒤에 "작업 완료!"를 출력한다.
setTimeout(() => {
resolve("작업 완료!");
}, 1000);
});
myPromise.then((result) => {
console.log(result); // "작업 완료!"
}).catch((error) => {
console.error(error);
}).finally(()=>{
console.log("항상 출력");
});
new Promise()로 프로미스를 생성하고 종료될 때까지 3가지 상태를 가진다.
Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태
만일 작업이 성공한다면 비동기 로직 실행이 참이라는 걸 알려주기 위해 resolve() 성공 메서드를 호출한다. 실패하면 reject() 메서드를 호출한다. resolve(data) 를 호출하게 되면, 바로 .then() 으로 이어져 then 메서드의 콜백 함수에서 성공에 대한 추가 처리를 진행한다. 이때 호출한 resolve() 함수의 매개변수의 값이 then 메서드의 콜백 함수 인자로 들어가 then 메서드 내부에서 프로미스 객체 내부에서 다룬 값을 사용할 수 있게 된다. 반대로 처리가 실패하여 프로미스 객체 내부에서 reject("Error") 를 호출하게 되면, 바로 .catch() 로 이어져 catch 메서드의 콜백 함수에서 추가 처리를 진행한다. 이때, 성공과 실패 여부와 상관없이 .finally()가 실행된다.
자바스크립트의 fetch() 메서드 내에서 프로미스 객체를 생성하여 서버로부터 데이터를 가져오면 resolve() 하여 .then() 으로 처리한다.
new Promise 객체를 사용하지 않아도 Promise를 반환한다.
await 키워드를 앞에 붙이면 해당 함수가 실행을 끝낼 때까지 다음 코드는 동작하지 않는다. 즉, Fulfilled 또는 Rejected가 될 때까지 동작하지 않는다. async가 붙은 함수 내에서만 쓸 수 있다. await를 사용하지 않은 상태에서의 함수 호출은 Promise가 반환된다. await를 사용하여 비동기 작업이 완료될 때까지 기다리면, Promise가 해결된 결과 값을 얻을 수 있다.
try...catch는 동기적 코드에서 발생하는 예외를 처리하는데 사용된다. 코드 블록 내에서 발생하는 예외를 잡아내어 이를 처리할 수 있게 한다. 비동기 코드에서 async/await와 try...catch를 결합하면, 비동기 작업을 마치 동기 코드처럼 작성할 수도 있다. 이 방법은 then...catch 대신 사용할 수 있으며, 코드가 더 간결하고 읽기 쉽다.
then...catch는 주로 Promise 객체를 사용한 비동기적 코드에서 사용된다. Promise가 성공했을 때(then)와 실패했을 때(catch)를 처리하는 데 사용된다.
후속 처리 메서드(then, catch)를 통해 반환되는 Promise를 체이닝하면, 여러 개의 Promise를 연결해서 사용 가능하다.
const promise = (result) => {
return new Promise((resolve, reject) => {
if(result === 'success')
resolve('성공');
else
reject('실패');
});
}
promise('success')
.then(result => promise(result))
.then(message => console.log('then : ' + message))
.catch(error => console.log('catch : ' + error));
주어진 값을 사용하여 이행된 Promise를 반환합니다.
만약 주어진 값이 Promise 객체라면, 해당 Promise를 반환합니다.
주어진 이유를 사용하여 거부된 Promise를 반환합니다
주어진 Promise 객체들의 iterable이 모두 이행되었을 때, 이행된 Promise를 반환합니다.
iterable 내의 어떤 Promise라도 거부되면, 즉시 거부된 Promise를 반환합니다
주어진 Promise 객체들의 iterable이 모두 완료되었을 때, 이행된 Promise를 반환합니다.
각 Promise의 결과를 객체 형태로 반환합니다. (이행 또는 거부 상관 없음)
주어진 Promise 객체들의 iterable 중 가장 먼저 완료된 Promise를 반환합니다. (이행 또는 거부 상관 없음)
주어진 Promise 객체들의 iterable 중 가장 먼저 이행된 Promise를 반환합니다.
모든 Promise가 거부되면, AggregateError를 발생시킵니다.
// Promise.resolve
Promise.resolve("성공")
.then(result => console.log("Promise.resolve:", result)); // "Promise.resolve: 성공"
// Promise.reject
Promise.reject("실패")
.catch(error => console.log("Promise.reject:", error)); // "Promise.reject: 실패"
// Promise.all
Promise.all([
Promise.resolve("성공1"),
Promise.resolve("성공2")
])
.then(results => console.log("Promise.all:", results)) // "Promise.all: ["성공1", "성공2"]"
.catch(error => console.log(error));
// Promise.allSettled
Promise.allSettled([
Promise.resolve("성공1"),
Promise.reject("실패")
])
.then(results => console.log("Promise.allSettled:", results));
// "Promise.allSettled: [{ status: "fulfilled", value: "성공1" }, { status: "rejected", reason: "실패" }]"
// Promise.race
Promise.race([
new Promise(resolve => setTimeout(() => resolve("성공1"), 100)),
new Promise(resolve => setTimeout(() => resolve("성공2"), 200))
])
.then(result => console.log("Promise.race:", result)); // "Promise.race: 성공1"
// Promise.any
Promise.any([
Promise.reject("실패1"),
Promise.resolve("성공1"),
Promise.resolve("성공2")
])
.then(result => console.log("Promise.any:", result)) // "Promise.any: 성공1"
.catch(error => console.log(error));
JavaScript 동기, 비동기 처리와 콜백 함수
📚 콜백 함수(Callback) 개념 & 응용 - 완벽 정리
자바스크립트 Promise 쉽게 이해하기