JavaScript
콜백에서 Promise와 async/await까지
나중에 얻을 결과를 Promise로 나타내고, 그 결과로 할 일을 함수로 연결합니다.
이럴 때 필요해요
서버에서 상품을 가져온 뒤 이름을 보여 주고 싶다면, 결과를 받기 전에 다음 일을 실행하지 않도록 순서를 정해야 합니다.
어떻게 동작하나요?
함수를 맡기는 콜백
함수를 다른 코드에 전달해 호출을 맡길 수 있습니다. 이 함수를 콜백이라고 합니다. 예를 들어 상품을 가져온 뒤 실행할 함수를 전달하면, 결과를 받은 코드가 그 함수를 호출해 상품을 넘겨줄 수 있습니다. 콜백 자체가 항상 나중에 실행되는 것은 아닙니다. 배열의 map에 주는 함수처럼 바로 실행되는 콜백도 있습니다.
나중에 받을 결과를 연결하기
Promise는 아직 기다리는 중인지, 결과를 얻었는지, 실패했는지를 나타내는 객체입니다. then에 함수를 전달하면 성공한 결과를 받아 다음 일을 할 수 있습니다. 이미 결과가 준비된 Promise라도 then의 함수는 현재 실행 중인 코드 사이에 끼어들지 않습니다. 아래 예제에서는 A와 C가 먼저 나오고 B가 나중에 나옵니다.
await로 순서 읽기
같은 순서를 위에서 아래로 읽고 싶으면 async 함수 안에서 await를 쓸 수 있습니다. await 뒤의 코드는 결과를 기다렸다가 이어집니다. 기다리는 동안 JavaScript 전체를 멈추는 것은 아닙니다. async 함수는 항상 Promise를 반환하며, await한 결과의 실패는 try/catch로 처리할 수 있습니다.
코드 예제
동작을 설명하는 읽기 전용 예제입니다.
console.log('A');
Promise.resolve('B').then(value => {
console.log(value);
});
console.log('C');
// 출력: A, C, BPromise.resolve로 B라는 결과를 준비하고 then에 출력 함수를 맡겼습니다. 현재 코드의 A와 C를 먼저 출력한 뒤 맡겨 둔 함수가 B를 출력합니다.
이 개념으로 실습하기
심화 원리와 적용 범위
여러 작업과 실패 처리
서로 독립적인 작업은 먼저 시작한 뒤 Promise.all로 결과를 모을 수 있습니다. 하나가 실패하면 합친 Promise도 실패하지만 나머지 작업까지 취소되지는 않습니다. 취소하려면 AbortSignal처럼 해당 작업이 지원하는 방법이 필요합니다. 반환하거나 await하지 않은 Promise의 실패도 별도로 처리해야 합니다. Promise를 만드는 함수의 본문(executor)은 즉시 실행되므로 긴 계산을 넣어도 다른 스레드로 옮겨지지 않습니다.
자주 헷갈리는 포인트
- 서로 독립적인 요청에 await를 연속으로 붙이면 앞 요청이 끝날 때까지 다음 요청이 순차적으로 대기하게 됩니다.
- 무거운 CPU 계산을 Promise로 감싼다고 해서 별도의 스레드에서 병렬로 실행되지는 않습니다.
내 말로 설명해 보기
Promise.all의 거부와 이미 시작된 나머지 요청의 취소는 왜 별개의 문제일까요?
생각할 단서 결과를 합치는 Promise와 이미 시작한 작업의 수명은 별개입니다.
예시 답변 확인
설명에 들어갈 핵심
- Promise.all은 입력 중 하나라도 거부되면 전체 결과가 즉시 거부됩니다.
- 이미 시작된 나머지 비동기 작업들은 자동으로 취소되지 않습니다.
- 작업을 실제로 중단하려면 AbortSignal처럼 해당 API가 지원하는 취소 수단을 전달해야 합니다.
예시 답변
Promise.all의 거부는 묶음 결과를 받지 못했다는 뜻입니다. 이미 시작한 나머지 요청은 계속될 수 있습니다. 취소하려면 각 요청이 AbortSignal 같은 계약을 지원해야 하고 그 신호를 실제로 전달해야 합니다.
문장을 그대로 외우기보다 예제의 값과 실행 흐름을 떠올리며 설명해 보세요.
실전 시나리오에 적용해 보기
함께 읽으면 좋은 개념
관련 면접 질문 6개
콜백 함수는 무엇이며 중첩된 비동기 코드를 어떻게 정리할까요?
콜백 함수는 다른 함수에 인자로 전달되어 특정 시점이나 조건에서 호출되도록 실행을 위임하는 함수입니다. 배열의 map이나 forEach처럼 즉시 실행되는 동기 콜백도 있으므로 콜백 자체가 곧 비동기를 뜻하지는 않습니다. 비동기 작업이 깊게 중첩되면 읽기 어렵고 에러 처리가 흩어지기 쉬운데, 이때 작업 단위로 함수를 분리하고 Promise 체인이나 async/await를 사용하면 실행 순서와 실패 처리 경로를 한눈에 보이도록 정리할 수 있습니다.
꼬리질문과 참고자료 →Promise의 상태와 Promise.all의 동작은 무엇인가요?
Promise는 비동기 작업의 최종 결과를 나타내는 객체로, 대기(pending) 상태에서 시작해 이행(fulfilled) 또는 거부(rejected) 중 하나로 한 번만 결정됩니다. 이후 then과 catch를 통해 성공과 실패 후속 처리를 체인으로 연결할 수 있습니다. 여러 비동기 작업을 묶는 Promise.all은 전달된 모든 Promise가 이행되면 완료 순서와 상관없이 입력한 순서대로 결과 배열을 반환하고, 하나라도 거부되면 전체 결과 Promise를 즉시 거부 상태로 만듭니다.
꼬리질문과 참고자료 →Promise는 콜백과 비교해 어떤 문제를 해결하나요?
콜백은 다음 동작을 다른 함수에 직접 넘기는 방식이라 비동기 단계가 많아지면 들여쓰기가 깊어지고 에러 처리가 단계마다 파편화되기 쉽습니다. 반면 Promise는 비동기 작업의 결과를 일급 객체로 표준화하여, then과 catch 체인을 통해 반환값과 에러를 일관된 흐름으로 전달합니다. 덕분에 성공 경로와 실패 경로를 구조적으로 합성하고 추론하기가 훨씬 쉬워집니다.
꼬리질문과 참고자료 →