본문 바로가기
면접준비

[면접-JS] Promise를 사용한 비동기 통신과 async, await를 사용한 비동기 통신의 차이를 설명해주세요.

by 강갱민 2024. 5. 3.

면접답변

Promise와 Async · Await의 차이점

에러 핸들링 측면에서는 Promise를 활용할 시에는 .catch() 문을 통해 에러 핸들링이 가능합니다.

Async · Await은 에러 핸들링 할 수 있는 기능이 없어 try-catch() 문을 활용해야합니다.

코드 가독성

Promise의 .then()을 활용하지만 코드가 길어지면 길어질수록, .then지옥으로 코드가독성이 않좋기에

Async · Await 을 활용한 코드가 가독성이 좋습니다.

 

 

자료

자바스크립트는 동기적으로 실행합니다.

즉 순차적으로 실행된다. 근데 만약에 아래처럼 무거운 함수가 있습니다.

function otherFunc({
	console.log('우리는 otherFunc안에 있습니다.)
})
    
console.log('Start')


otherFunc()

for(let i =0; i< 100000000; i++){
	console.log(i);
}
   
console.log(end)

그럼 엔드가 출력되기위해 저 무거운 함수를 다돌아야 합니다.

 

1. 자바스크립트에서 비동기 처리는 왜 필요할까?

자바스크립트는 비동기언어이다. 즉, 코드 하나가 처리될때 지연시간이 발생한다면 이후의 태스크들은 블로킹 되어집니다. 서버에 데이터를 요청하는 등의 대기시간이 긴 작업의 경우에는 지연기간을 해서하기위한 병렬적 함수 실행이 필요합니다.

이때 나오는 개념이 비동기처리입니다.

 

function getData() {
    // 비동기적으로 데이터를 가져오는 네트워크 요청을 가정
    fetch('https://example.com/data')
        .then(response => response.json())
        .then(data => {
            return data;  // 이 라인은 fetch의 결과 처리가 완료된 후에 실행됩니다.
        });
    // 여기서 함수는 fetch 요청이 완료되기를 기다리지 않고 끝나므로, 반환값이 없습니다.
}

function printData() {
    const data = getData();
    console.log(data);  // getData()에서 바로 반환된 값이 없으므로, undefined가 출력됩니다.
}

 

 

위의 코드에서 getData() 함수가 서버에서 데이터를 가져오는 함수라고 가정 할때 getData()fetch 요청의 결과를 기다리지 않고 종료되므로, printData()에서 console.log(data);를 실행했을 때, getData()에서 반환되는 값은 undefined입니다. 이 문제를 해결하려면 getData()에서 Promise를 반환하게 하고, printData()에서 이 Promise를 기다릴 필요가 있습니다:

 

2. 비동기 처리 방식  

비동기 처리방식은 크게 async - await 방식 pomise 방식을 활용한다.

 

Promise에 비해 가독성이 좋고, 문법이 간결하다.

 

아래의 코드는 promise 방식과 async - await 방식을 활용한 코드입니다.

 

promise

function getText() {
    return new Promise((resolve, reject) => {
        $.get("API 주소", function(res) {
            if (res) {
                resolve("데이터를 성공적으로 불러왔습니다");
            } else {
                reject("데이터를 불러오는데 실패했습니다");
            }
        });
    });
}

function printText() {
    getText()
        .then(data => {
            console.log(data);
        })
        .catch(error => {
            console.error(error);
        });
}

 

async - await

function getText() {
	return newData((resolve, reject) => {
    	$.get("API 주소", function((res) => {
        	if (res) {
            	resolve("데이터를 성공적으로 불러왔습니다");
            }
        });
        reject("데이터를 불러오는데 실패했습니다");
    });
}

async function printText() {
	const text = await getText();
    console.log(text);
}

 

이렇게 짧은 코드에서는 가독성과 간결성이 async - await가 좋다는 것을 느끼기 힘듭니다.

하지만 추가적인 데이터 처리방식이 들어가면 promise는 .then을 통해 많은 들여쓰기와 복잡한 코드로 보입니다.

반면  Async · Await과 같은 경우, 응답 값을 명시적인 변수에 담아 사용하므로 직관적인 변수를 인식할 수 있으며, 간결하다는 장점을 가지고 있습니다.

 

function getText() {
	return newData((resolve, reject) => {
    	$.get("API 주소", function((res) => {
        	if (res) {
            	resolve({name : "홍길동", property : "홍길동의 특성"});
            }
        });
        reject("데이터를 불러오는데 실패했습니다");
    });
}

function printText_Promise() {
	return getText()
    .then((data) => {
    	if (data.property) {
        	return sampleFunc1(data)
            .then(anotherData => {
            	console.log(anotherData);
            }
        } else {
        	console.log(data);
        }
    }
}

async function printText_Async() {
	const text = await getText();
    if (text.property) {
    	const sampleData = await sampleFunc1(text);
        console.log(sampleData);
    } else {
    	console.log(text);
    }
}

 

 

에러헨들링에 유용

function fetchData() {
    return new Promise((resolve, reject) => {
        $.get("API_URL", function(response) {
            if (response) {
                resolve({name: "홍길동", property: "특성"});
            } else {
                reject("데이터 로드 실패");
            }
        }).fail(() => {
            reject("API 요청 실패");
        });
    });
}

function displayDataUsingPromise() {
    fetchData()
        .then(data => {
            try {
                const person = JSON.parse(data);
                console.log(person);
            } catch (parseError) {
                console.error("JSON 파싱 에러:", parseError);
            }
        })
        .catch(error => {
            console.error("에러 발생:", error);
        });
}

async function displayDataUsingAsync() {
    try {
        const data = await fetchData();
        const person = JSON.parse(data);
        console.log(person);
    } catch (error) {
        console.error("에러 발생:", error);
    }
}

위의 코드에서 try 문에 들어가있는 .catch 코드, try 문의 .catch 코드가 보일 것이다. 코드가 직관적이지 않으며 동시에 에러를 처리하는 catch 문의 중복성이 눈에 띄고 있다.

 

1.Promise 예제: .catch()를 사용하여 오류를 캡처합니다. 이 방법은 여러 .then() 호출이 체인될 때 각 단계에서 발생할 수 있는 오류를 한 곳에서 처리할 수 있게 해줍니다. 그러나, 각 .then()에서 발생할 수 있는 복잡한 오류를 구체적으로 파악하고 디버그하기는 더 어려울 수 있습니다.

// Promise를 사용한 예제
function samplePromise() {
    return sampleFunc()
        .then(data => data)
        .catch(err => {
            console.error("Promise에서 오류 발견:", err);
        });
}

 

 

2. async/await 예제: try...catch 블록을 사용하여 오류를 처리합니다. 이 방식은 일반적인 동기 코드의 오류 처리와 매우 유사하며, 어떤 함수 호출에서 오류가 발생했는지 정확하게 알 수 있게 해줍니다. 코드의 흐름을 더 쉽게 따라갈 수 있으며, 각 단계에서의 오류 처리를 개별적으로 할 수 있습니다.


// async/await를 사용한 예제
async function sampleAsync() {
    try {
        const data = await sampleFunc();
        console.log(data);
    } catch (err) {
        console.error("async/await에서 오류 발견:", err);
    }
}

그러면 Promise 비에 async/await이 압도적으로 좋은데 promise 안쓰냐 ?

promise 장점

 

1. 병렬 처리

Promise는 **Promise.all()**과 같은 정적 메소드를 통해 여러 비동기 작업을 병렬로 실행하고, 모든 작업이 완료되기를 기다린 후 결과를 한 번에 처리할 수 있는 기능을 제공합니다. 이는 여러 리소스를 동시에 가져오거나, 여러 API 호출의 결과가 동시에 필요한 경우 매우 효율적입니다. **async/await**도 이러한 패턴을 사용할 수 있지만, Promise를 사용하는 것이 더 자연스러울 수 있습니다.

2. 커스텀 비동기 흐름 제어

Promise를 사용하면 비동기 작업의 흐름을 더 세밀하게 제어할 수 있습니다. 예를 들어, 비동기 작업의 체이닝, 조건부 실행, 또는 동적으로 Promise를 생성하고 관리하는 복잡한 시나리오에서는 Promise가 더 유연한 해결책을 제공할 수 있습니다.

3. 에러 처리의 유연성

**async/await**를 사용할 때는 try/catch 블록으로 에러를 처리합니다. 이는 매우 직관적이고 편리하지만, 모든 에러를 동일한 방식으로 처리해야 하는 경우에는 제한적일 수 있습니다. 반면, Promise는 .catch() 메소드를 통해 각각의 비동기 작업에 대해 보다 세밀한 에러 처리를 구현할 수 있습니다. 또한, .then(), .catch(), .finally() 메소드를 통해 성공, 실패, 완료 시점에 실행할 콜백 함수를 지정하는 것이 더 명확할 수 있습니다.

4. 프로미스 컴포지션

Promise를 사용하면, 작은 단위의 비동기 작업을 컴포지션하여 더 큰 비동기 프로세스를 구성할 수 있습니다. 이는 코드의 재사용성과 모듈성을 향상시킬 수 있으며, 복잡한 비동기 로직을 더 관리하기 쉽게 만들어 줍니다.

5. 호환성

구형 JavaScript 환경이나 라이브러리에서는 **async/await**를 지원하지 않을 수 있습니다. 이 경우, Promise는 여전히 비동기 작업을 효과적으로 처리할 수 있는 유일한 수단일 수 있습니다.

결론

**async/await**가 일반적인 사용 사례에서 코드의 가독성과 작성 편의성을 크게 개선하지만, Promise는 병렬 처리, 커스텀 비동기 흐름 제어, 세밀한 에러 처리 등 특정 상황에서 더 나은 선택이 될 수 있습니다. 따라서, 두 기술은 상호 배타적인 것이 아니라 상호 보완적이며, 상황에 따라 적절히 선택하여 사용하는 것이 중요합니다.

 

 

참고자료
https://klmhyeonwooo.tistory.com/54?category=1135084

https://www.youtube.com/watch?v=gZ7kISGdHa0




'면접준비' 카테고리의 다른 글

[면접-JS] var,let,const 차이 with호이스팅  (0) 2024.05.08