동기(Synchronous)
동기란 각 참여자가 즉시(또는 가능한 한 즉시) 메시지를 수신(필요시 처리 및 회신)하는 실시간 통신을 말합니다.
- 요청 결과가 동시에 일어납니다.
- 작업의 순서를 보장 여러 요청을 동시에 처리 할 수 없음 (1:1 방식)
- Blocking 방식
비동기(Asynchronous)
비동기는 주로 두 개 이상의 객체나 이벤트가 동시에 존재하지 않거나 발생하지 않는 것을 의미합니다 (여러 관련된 작업이 원래의 일이 끝날 때까지 기다리지 않는 것도 포함됩니다).
- 요청 결과가 동시에 일어나지 않습니다.
- 요청 후 결과와 상관없이 다음 요청을 진행 여러 요청을 동시에 처리 (1:N 방식)
- Non-Blocking 방식
비동기 방식은 비동기 요청시 응답 후 처리할 "콜백 함수"를 알려줍니다. 따라서, task가 완료되었을 때, "콜백 함수"를 호출합니다. 하지만 비동기 처리를 위해 여러개의 콜백 패턴을 사용하게 되면 처리 순서를 보장하기 위해 여러 개의 콜백 함수가 중첩이 되어서 "콜백 헬(Callback Hell)"이 발생하는 단점이 있습니다. 콜백 헬은 가독성을 나쁘게 하며 실수를 유발할 수도 있습니다.
>>>
비동기 함수(Asynchronous function)는 함수의 실행 결과가 즉시 반환되지 않고, 특정 조건이 충족될 때까지 기다리는 함수입니다. 비동기 함수는 일반적으로 콜백 함수나 Promise 객체를 반환합니다. 콜백 함수는 비동기 작업이 완료되었을 때 호출되는 함수이며, Promise 객체는 비동기 작업이 성공적으로 완료되었는지 또는 실패했는지를 나타내는 객체입니다. 비동기 함수는 애플리케이션의 성능을 향상시키고, 사용자 인터페이스의 반응성을 유지하는 데 유용합니다. 그러나 비동기 함수를 사용할 때는 콜백 지옥과 같은 문제를 피하기 위해 적절한 에러 처리와 코드 구성이 필요합니다. 여러개의 비동기 함수가 실행되면, 이벤트 루프는 비동기 함수 호출을 처리하고, 비동기 함수가 완료되었을 때 콜백 함수를 호출합니다. 이벤트 루프는 실행 대기 중인 비동기 함수가 있으면 해당 함수를 호출하고, 실행이 완료될 때까지 다음 비동기 함수를 호출합니다. 따라서, 여러 개의 비동기 함수가 실행될 때 이들 함수는 동시에 실행되며, 이벤트 루프에 의해 비동기 함수의 실행 순서가 제어됩니다.
Promise
프로미스(Promise)는 프로미스가 생성된 시점에는 알려지지 않았을 수도 있는 값을 위한 대리자로, 비동기 연산이 종료된 이후에 결과 값과 실패 사유를 처리하기 위한 처리기를 연결할 수 있습니다. 프로미스를 사용하게 되면 비동기 메서드에서 마치 동기 메서드 처럼 값을 반환할 수 있습니다. 다만 최종 결과를 반환하는 것이 아니고, 미래의 어떤 시점에 결과를 제공하겠다는 "약속" 즉, 프로미스를 반환합니다.
Promise는 하나의 상태 값을 가집니다.
1) 대기(pending) : 이행하지도, 거부하지도 않은 초기 상태
2) 성공(fulfilled) : 연산이 성공적으로 완료
3) 실패(rejected) : 연산이 실패
다음 세 가지 상태 중 하나의 상태를 가지며 연산이 성공적으로 완료되었을 때 이행(resolve) 함수를 호출하고, 오류가 발생하면 거부(reject)함수를 호출합니다. 이행이나 거부될 때 프로미스의 then 메서드에 의해 대기열에 추가된 처리기들이 호출됩니다. 이미 이행했거나 거부된 프로미스에 처리기를 연결해도 호출되므로, 비동기 연산과 처리기 연결 사이에 경합 조건은 없습니다.
아래 간단한 코드 작성 예시입니다.
const promiseSuccess = new Promise((resolve, reject) => {
resolve('Success!');
});
promiseSuccess.then((value) => {
console.log(value);
// Expected output: "Success!"
});
const promiseReject = new Promise((resolve, reject) => {
throw new Error('Uh-oh!');
});
promiseReject.catch((error) => {
console.error(error);
});
// Expected output: Error: Uh-oh!
또한, 체이닝을 통해 기존의 콜백지옥에서 벗어날 수 있습니다.
a(function (resultA) {
b(resultA, function (resultB) {
c(resultB, function (resultC) {
d(resultC, function (resultD) {
//무한콜백지옥........
});
});
});
});
promise.then(function(a){
}).then(function(b){
}).then(function(c){
}).then(function(d){
});
async / await
async 함수 선언은 AsyncFunction 객체를 반환하는 하나의 비동기 함수를 정의합니다.
콜백과 Promise의 단점을 보안하기 위해서 ES8부터 출시했으며 기존의 콜백지옥, then지옥을 해결하고자 사용됩니다.
const func = async () => {
const condition = true;
const promise = new Promise((resolve, reject) => {
if (condition) {
resolve('resolved');
} else {
reject('rejected');
}
});
try {
const result = await promise;
console.log(result);
} catch (err){
console.error(err);
}
};
프로미스의 사용코드를 async / await 사용 방법으로 바꿨습니다. async 함수 내에서 await를 통해 promise를 반환하며 result 변수에 담아 콘솔에 출력을 했습니다. async / await문은 자체적으로 에러 핸들링을 할 수 없어서 try / catch를 통해 에러 핸들링을 할 수 있습니다.
정리
자바스크립트는 동기방식이지만 왜 비동기방식이 필요한지 알았습니다. 그에 따른, 비동기 방식에서 사용되는 여기에는 없는 setTimeout()에서 Callback, Promise, async / await에 대해서도 학습을 할 수 있었습니다.
우선, 비동기방식이 사용되는 이유는 간단하게 더 빠르기 때문이고 서버에 데이터를 요청하는 대기시간이 긴 작업때문입니다. 또한, 그에 따른 순서를 보장하기 위해 위와 같은 방식이 사용되고 점점 진화와 문제점을 해결해서 현재 async / await 비동기 처리를 한다고 생각하면 될 것 같습니다.
async/await는 비동기적인 작업을 처리할 수 있는 ES2017 문법 입니다. async 함수를 정의하면 함수 내부에서 await 키워드를 이용하여 비동기적으로 처리되는 작업이 완료될 때까지 기다린 후, 결과값을 반환하는 처리를 할 수 있습니다. async/await는 Promise를 기반으로 하며, 코드를 보다 간결하고 직관적으로 작성할 수 있도록 해줍니다. async 함수는 항상 Promise 객체를 반환하며, await 키워드를 이용하여 비동기 처리 결과를 기다립니다.
참조
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise
Promise - JavaScript | MDN
Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.
developer.mozilla.org
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/async_function
async function 표현식 - JavaScript | MDN
async function 키워드는 표현식 내에서 async 함수를 정의하기 위해 사용됩니다.
developer.mozilla.org
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/await
await - JavaScript | MDN
Promise 혹은 기다릴 어떤 값입니다.
developer.mozilla.org