Promise, async, await

담당
김영웅
완료
완료
유형
JavaScript
비고

요약

Promise에 대해 설명해주세요.
ES6에서 비동기 처리를 위해 Promise를 도입했습니다.
Promise는 콜백 패턴이 가진 단점인 콜백 지옥을 해결하기 위해 then 체이닝 기능을 제공합니다.
또한 콜백 패턴에 비해 비동기 처리 시점이 명확하게 표현되고, catch 메서드를 사용해서 에러 처리도 명확하게 할 수 있습니다.
Promise의 세 가지 상태는 각각 무엇이며, 어떤 의미를 가지나요?
  • pending(대기): 비동기 처리 로직이 아직 완료되지 않은 상태
  • fulfilled(이행): 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
  • rejected(실패): 비동기 처리가 실패하거나 오류가 발생한 상태
Promise 실행 함수가 가지고 있는 두 개의 파라미터 resolve와 reject는 각각 무엇을 의미하나요?
  • resolve 는 Promise가 성공적으로 완료되었을 때 호출되는 함수입니다. Promise의 결과값을 전달하며, Promise의 상태를 fulfilled로 변경합니다.
  • reject는 Promise가 실패했을 때 호출되는 함수입니다. Promise의 실패 원인을 전달하며, Promise의 상태를 rejected로 변경합니다.
Promise.all과 Promise.race의 차이점은 무엇인가요?
두 메서드는 여러 개의 프로미스 객체를 동시에 실행한다는 점이 같습니다.
하지만 all은 모든 객체를 실행하다가 하나라도 reject가 된 프로미스 객체가 있다면 즉시 해당 에러를 반환합니다.
이것은 모든 프로미스 객체가 resolve 상태가 될 때까지 기다린 후 결과를 반환한다는 점에서 동기 처리와 비슷하다고 할 수 있습니다.
race는 모든 객체를 실행하다가 먼저 resolve가 된 프로미스 객체의 결과값을 반환합니다.
  • 만약 promise1은 1초 뒤에 resolve를, promise2은 1초 뒤에 reject를 반환한다면 Promise.race는 어떤 것을 반환할까요?
    • const promise1 = new Promise(resolve => setTimeout(resolve, 1000, 'foo')); const promise2 = new Promise((_, reject) => setTimeout(reject, 1000, 'bar')); Promise.race([promise1, promise2]) .then(result => console.log(result)) // 'foo' .catch(error => console.error(error)); Promise.race([promise2, promise1]) .then(result => console.log(result)) // 'foo' .catch(error => console.error(error)); ////////// const promise2 = new Promise((_, reject) => setTimeout(reject, 1000, 'bar')); const promise1 = new Promise(resolve => setTimeout(resolve, 1000, 'foo')); Promise.race([promise1, promise2]) .then(result => console.log(result)) // 'foo' .catch(error => console.error(error));
async, await이 무엇인가요?
async 선언은 AsyncFunction객체를 반환하는 하나의 비동기 함수를 정의합니다.
비동기 함수는 이벤트 루프를 통해 비동기적으로 작동하는 함수로, 암시적으로 Promise를 사용하여 결과를 반환합니다.
await는 async 함수에서만 사용할 수 있습니다. await를 사용하면 Promise가 처리될 때 까지 함수 실행을 기다렸다가 Promise가 처리되면 결과와 함께 실행이 재개됩니다.
await를 사용하면 비동기를 동기처럼 보이게 해 코드의 가독성을 좋게 하고 간결하게 사용할 수 있습니다.
Promise와 async, await의 차이를 설명해주세요.
Promise는 비동기 작업의 결과를 나타내는 객체이며, asyncawaitPromise를 사용하는 코드를 보다 간결하고 직관적으로 작성할 수 있게 해주는 문법적인 기능입니다.
 

Promise

ES6에서 비동기 처리를 위해 Promise를 도입했습니다.
Promise는 콜백 패턴이 가진 단점인 콜백 지옥을 해결하기 위해 then 체이닝 기능을 제공합니다.
또한 비동기 처리 시점이 명확하게 표현되고, catch 메서드를 사용해서 에러 처리도 명확하게 할 수 있습니다.
 

호출 과정

  • 비동기 함수 안에서 Promise 객체를 생성하고, 객체 안에서 비동기 처리를 구현합니다.
    • 처리 성공: resolve 메서드 호출 → resolve 메서드의 인자로 비동기 처리 결과를 전달 → Promise 객체의 후속 처리 메서드 then 으로 전달
    • 처리 실패: reject 메서드 호출 → reject 메서드 인자로 에러 전달 → Promise 객체의 후속 처리 메서드 catch 로 전달
 

Promise의 처리 흐름

notion image
 

Promise의 세 가지 상태

pending (대기)

처음 Promise가 생성되면 pending 상태이고, 비동기 처리 로직이 완료되지 않은 상태입니다.

fulfilled (이행)

Promise에 담긴 콜백 함수에서 resolve 함수가 실행되면 fulfilled 상태로 변경됩니다.
fulfilled 상태가 되었다면 then 메서드를 이용해 로직을 처리하고 값을 반환받을 수 있습니다.

rejected (실패)

reject 함수가 실행되었다면 rejected 상태로 변경됩니다.
rejected 상태가 되었다면 catch 메서드를 이용해 에러 핸들링을 할 수 있습니다.
 

resolve와 reject

resolve 는 Promise가 성공적으로 완료되었을 때 호출되는 함수입니다. Promise의 결과값을 전달하며, Promise의 상태를 fulfilled로 변경합니다.
resolve 함수에서 전달된 인자는 then메서드의 콜백 함수의 첫 번째 매개변수로 전달됩니다.
 
reject는 Promise가 실패했을 때 호출되는 함수입니다. Promise의 실패 원인을 전달하며, Promise의 상태를 rejected로 변경합니다.
reject함수에서 전달된 인자는 catch메서드의 콜백 함수의 첫 번째 매개변수로 전달됩니다.
 

Promise의 메서드

then

Promise 객체가 성공적으로 처리되었을 때 호출됩니다.
then 메서드의 리턴 값?
  • 새로운 Promise 객체를 반환합니다. 이 새로운 Promise 객체는 이전 Promise 객체에서 then()메서드에 전달한 콜백 함수가 반환한 값 또는 then()메서드에 전달한 콜백 함수가 반환하지 않은 경우, 이전 Promise 객체가 반환한 값을 resolve 값으로 갖습니다.

catch

Promise 객체가 실패했을 때 호출됩니다.
catch 메서드의 리턴 값?
  • 새로운 Promise 객체를 반환합니다. 이 새로운 Promise 객체는 이전 Promise 객체에서 발생한 reject 값을 처리합니다.

finally

Promise 객체가 resolve되던 reject되던 상관없이 지정된 함수를 실행합니다. Promise의 결과에 상관없이 동작 해야할 때 유용합니다.

Promise.all

모든 Promise를 실행한 후 진행되는 하나의 Promise를 반환합니다. 각각 catch해줄 필요는 없고 전체에 하나 해주면 됩니다.

Promise.race

Promise들 중에서 가장 먼저 완료된 것의 결과값을 이행하거나 거부합니다. Promise.all과 사용 방법은 같습니다.

Promise.allSettled

주어진 모든 프로미스를 이행하거나 거부한 후, 각 프로미스에 대한 결과를 나타내는 객체 배열을 반환합니다.
일반적으로 서로의 성공 여부에 관련 없는 여러 비동기 작업을 수행해야 하거나, 항상 각 프로미스의 실행 결과를 알고 싶을 때 사용합니다.

Promise.any

주어진 모든 프로미스 중 하나라도 이행하는 순간, 즉시 그 프로미스의 값으로 이행하는 새로운 프로미스를 반환합니다.
 

그 외

콜백 함수와 프로미스의 차이점은 무엇인가요?
Callback
  • 함수가 비동기적으로 실행될 때 콜백 함수를 전달하고, 작업이 완료되면 콜백 함수가 호출됩니다.
  • 콜백 함수 안에서 예외 처리를 직접 해주어야 합니다.
  • 콜백 지옥(callback hell)이 발생할 수 있습니다.
Promise 객체의 then 메서드에서 또 다른 Promise 객체를 반환하는 것은 어떻게 동작하나요?
반환된 Promise 객체가 처리된 후 원래 Promise 객체의 후속 처리 메서드에서 처리 결과가 처리됩니다.
setTimeout이 호출되고 그 다음 Promise가 호출되면 무엇이 먼저인가요?
마이크로 태스크 큐에 있는 Promise가 먼저 나옵니다.
console.log('script start'); // A setTimeout(function () { // B console.log('setTimeout'); }, 0); Promise.resolve() .then(function () { // C console.log('promise1'); }) .then(function () { // D console.log('promise2'); }); console.log('script end'); // E // script start // script end // promise1 // promise2 // promise3 // setTimeout
1. 콜 스택에는 전역 실행 객체가 있고, '스크립트 실행'이라는 태스크가 매크로 태스크 큐에 들어있다.
2. 이벤트 루프는 매크로 태스크 큐에 있는 '스크립트 실행' 태스크를 실행한다.
3. A에 도달하면, 'script start'가 출력된다.
4. B에 도달하면, setTimeout web api가 타이머를 실행시키고, 타이머가 종료되면 콜백 함수가 매크로 태스크 큐에 들어간다.
5. C에 도달하면, 콜백 함수가 마이크로 태스크 큐에 들어간다.
6. E에 도달하면, 'script end'가 출력된다.
7. 콜 스택이 비었으므로, 이벤트 루프는 마이크로 태스크 큐에 있는 프라미스 콜백 함수를 실행시킨다.
8. 'promise 1'이 출력된다.
9. Promise.then 메서드는 D 콜백 함수를 마이크로 태스크 큐에 등록한다.
10. 이벤트 루프는 다음 마이크로 태스크인 D 콜백 함수가 실행시킨다.
11. 'promise 2'가 출력된다.
12. 렌더링할 것이 있으면, 브라우저는 렌더링을 한다.
13. 매크로 태스크 큐에 있는 setTimeout 콜백함수를 실행시킨다.
14. 'setTimeout'이 출력된다.
 

async

async function 선언은 AsyncFunction객체를 반환하는 하나의 비동기 함수를 정의합니다.
비동기 함수는 이벤트 루프를 통해 비동기적으로 작동하는 함수로, 암시적으로 Promise를 사용하여 결과를 반환합니다.
async 키워드는 function 함수 앞에 사용할 수 있습니다.
Promise가 아닌 값을 반환하더라도 이행 상태의 Promise로 값을 감싸서 반환합니다.
  • 원시 값 반환할 때
    • notion image
  • Promise 반환할 때
    • notion image
 

await

await는 async 함수에서만 동작합니다.
await를 사용하면 Promise가 처리될 때 까지 함수 실행을 기다렸다가 Promise가 처리되면 결과와 함께 실행이 재개됩니다. Promise가 처리되길 기다리는 동안 다른 태스크를 처리할 수 있기 때문에 효율적입니다.
await를 사용하지 않으면 콜백 함수나 then()메서드를 사용해야 합니다. 그러면 사용하기도 불편하고 코드의 가독성도 안좋아질 것입니다.
await를 사용함으로써, 비동기를 동기처럼 보이게 해 코드의 가독성을 좋게하고 간결하게 사용할 수 있습니다.
 
await 키워드 다음에 등장하는 함수 실행은, 어떤 타입을 리턴할 경우에만 의미가 있나요?
Promise객체를 반환하는 경우에만 의미가 있습니다.
Promise객체가 아닌 다른 값을 반환하는 함수를 await 키워드로 호출하면, 해당 값을 바로 반환합니다.
이는 일반적으로 동기적인 호출과 다를 바가 없어, await 키워드가 필요하지 않습니다.
따라서, await키워드를 사용하여 함수를 실행하려면 해당 함수가 Promise객체를 반환하도록 구현되어야 합니다.
await 키워드를 사용할 경우, 어떤 값이 리턴되나요?
Promise에서 fulfill된 값이 리턴됩니다. 만약 reject 된다면 에러를 throw합니다.
async/await를 사용할 때 에러를 처리하는 방법은 무엇인가요?
  • try-catch 구문을 사용하는 방법
    • const func = async () => { try { const response = await fetch("https://strange"); } catch (err) { console.log(err); } } func(); // TypeError: Failed to fetch
  • Promise 객체의 catch 메서드를 사용하는 방법
    • async 함수 내부에서 발생한 에러는 Promise 객체로 처리되기 때문에, catch 메서드를 사용할 수 있습니다.
async function fetchData() { const result = await fetch('https://example.com/data'); const data = await result.json(); console.log(data); } fetchData() .catch(error => console.error(error));
 

참고