정의
동기(Synchronous)란?
동기란 요청의 응답에 따라 순차적으로 작업을 수행하는 통신이다.
A 요청과 B 요청이 있다고 가정했을 때 A 요청을 보내고 A 응답을 받고 B 요청을 보내고 B 요청을 받는 것 처럼 순차적으로 작업을 수행하게 되는 것을 동기 통신이라고 한다.
즉, 작업에 대한 순서를 보장하는 통신이라고 생각든다.동기 통신 같은 경우 순서를 무조건 적으로 보장 해야하는 작업을 할 경우 용이하다. 하지만 데이터베이스와의 통신 또는 다중 작업을 진행 할 경우 한 개의 통신이 끝나기 전까진 다른 통신은 하지 못하니 비효율 적일 것이다.
비동기(Asynchronous)란?
비동기란 요청의 응답에 따라 순차적으로 진행하지 않아도 되는 통신이다.
A 요청과 B 요청이 있다고 가정했을 때 A 요청을 보내고 B 요청을 보내고 B 요청을 받고 A 요청을 받게 되는 것 처럼 꼭 순차적으로 작업을 수행하고 응답을 받지 않아도 되는 것을 비동기 통신이라고 한다. 즉, 작업에 대한 순서를 보장하지 않고 자유롭게 통신한다고 보면 된다.
비동기 통신 같은 경우 순서를 보장하지 않는 다중 작업을 할 경우 용이하다.
비동기 통신 시 통신에 대한 응답이 오면 추후 처리를 하는 Callback 함수라는 것이 호출된다.
Callback 함수를 너무 과도하게 사용하게 되면 중첩도가 많아져 복잡해지는 콜백 지옥에 빠지게 된다.
-> 조심해야 할 부분
자바스크립트에서는 setTimeout() 함수가 대표적인 비동기 처리 방식이고, 그 외에도 Promise, Async/Await, Ajax, fetch, axios 방식이 있다.
왜 setTimeout 이 비동기 처리 방식일까? -> setTimeout 은 인자값으로 밀리초, 함수를 받게 되는 데 보통 코드는 맨 처음 라인부터 실행하기 마련인데 setTimeout 함수를 사용해서 처음 라인에 대한 응답을 받기도 전인 세번 째,두번 째 라인의 값 부터 출력 되었음을 알 수 있다.
setTimeout(() => {console.log("첫번째 라인")}, 5000);
setTimeout(() => {console.log("두번째 라인")}, 3000);
setTimeout(() => {console.log("세번째 라인")}, 1000);
// 출력
세번째 라인
두번째 라인
첫번째 라인
Promise 란?
비동기 방식은 콜백 함수를 사용하게 되는 데 콜백 함수를 사용하게 되면 콜백 지옥으로 인해 가독성도 떨어지고 비동기 처리 중 발생한 에러 처리에도 문제가 있어 여러 개의 비동기 처리를 한 번에 하기가 어렵다.
Promise는 전통적인 콜백 패턴을 보완하여 비동기 처리 시점을 명확하게 처리할 수 있다.
Promise는 Promise 생성자 함수를 통해 인스턴스화 하여 비동기 작업을 수행할 콜백 함수를 인자 값으로 받고, 이 콜백 함수는 resolve와 reject 함수를 인자로 전달받아 처리한다.
// Promise 객체의 생성
const promise = new Promise((resolve, reject) => {
// 비동기 작업을 수행한다.
if (/* 비동기 작업 수행 성공 */) {
resolve('result');
}
else { /* 비동기 작업 수행 실패 */
reject('failure reason');
}
});
resolve 함수는 비동기 작업이 성공했음을 알려주는 것이고, reject 함수는 비동기 작업이 실패했음을 알려주는 것이다.
promise는 일반적인 비동기 작업과는 다르게 작업이 성공하거나 실패할 시의 promise가 끝나고 난 뒤의 동작을 설정 가능하다. 그것이 바로 then 과 catch 이다.
const promise1 = new Promise((resolve, reject) => {
resolve();
});
promise1
.then(() => {
console.log("then!");
})
.catch(() => {
console.log("catch!");
});
.finally(() => {
console.log("finally!");
});
then 같은 경우 Promise가 성공 했을 시의 동작을 지정한다. 인자로 함수를 받는다.
catch 같은 경우 Promise가 실패 했을 시의 동작을 지정한다. 인자로 함수를 받는다.
finally 같은 경우 Promise가 성공 하던 실패하던 동작이 마무리 되면 무조건 적으로 실행되는 동작이다.
promise에서 resolve 함수를 호출할 경우 자연스레 then이 콜백 함수처럼 동작해 then! 이 log에 찍힐 것이고
reject 함수를 호출할 경우 catch가 동작해 catch! 가 log에 찍힐 것이다.
promise에서는 비동기 통신 이후 콜백 함수를 그냥 동작하는 것이 아니라 비동기 통신의 성공 실패 여부에 따라서 추후 동작하는 함수를 달리 할 수 있다는 점이다.
보통의 Promise는 아래와 같이 사용하게 된다.
// 프로미스 객체를 반환하는 함수 생성
function myPromise() {
return new Promise((resolve, reject) => {
if (/* 성공 조건 */) {
resolve(/* 결과 값 */);
} else {
reject(/* 에러 값 */);
}
});
}
// 프로미스 객체를 반환하는 함수 사용
myPromise()
.then((result) => {
// 성공 시 실행할 콜백 함수
})
.catch((error) => {
// 실패 시 실행할 콜백 함수
});
프로미스 객체를 반환하는 함수를 먼저 생성 해 놓고 프로미스 객체를 반환하는 함수를 사용하여 then 또는 catch 또는 finally 를 사용해 더욱 눈에 보기 좋게 비동기 통신을 진행할 수 있다.
허나 프로미스 또한 then을 지속적으로 사용하면 데이터의 원본을 알아보기 어려울 정도가 될 수도 있다.
fetch("https://api.github.com/users")
.then((response) => {
if (response.ok) {
return response.json();
} else {
throw new Error("Network Error");
}
})
.then((users) => {
return users.map((user) => user.login);
})
.then((logins) => {
return logins.join(", ");
})
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error);
});
then을 여러번 중첩해서 원하는 결과를 얻을 수 있으나 이 또한 Promise 지옥이 될 수 있다고 하니 주의가 필요하다.
그리하여 이러한 Promise 지옥을 극복하기 위해서 사용하는 것이 async 와 await 이다.
async와 await는 함수 앞에 async 를 붙이게 되면 자동으로 Promise 객체가 반환되어 훨씬 간단하다.
// 일반 Promise 사용
function promiseFunc() {
return new Promise((resolve, reject) => {
resolve('Promise is awesome')
})
}
promiseFunc().then(console.log)
// async 사용
async function asyncFunc() {
return 'Async is awesome'
}
asyncFunc().then(console.log)
이런 방식으로 비동기 통신이 가능하다.
그리고 Promise를 반환하는 함수 앞에 await 를 붙이게 되면 간단하게 동기식으로 통신을 할 수 있다.
const timeout = (value, timeout) => new Promise((resolve, reject) => {
setTimeout(() => resolve(value), timeout)
})
async function awaitFunc() {
let str = ''
str += await timeout('Hello ', 1000) // 프로그램 실행후 1초뒤에 수행됨
console.log('complete promise')
str += await timeout('My name is ', 2000) // 프로그램 실행후 3초뒤에 수행됨(1 + 2)
console.log(str)
console.log('complete promise')
str += await timeout('nowon ', 3000) // 프로그램 실행후 6초뒤에 수행됨(1 + 2 + 3)
console.log('complete promise')
return str
}
awaitFunc().then(console.log)
위 코드에서 async 로 선언된 awaitFunc() 함수안에 await로 Promise를 반환하는 함수인 timeout() 을 실행해 str 값을 계속 추가 해주는 코드이다.
// 출력
complete promise
Hello My name is
complete promise
complete promise
Hello My name is nowon
await 가 모두 실행되기 전인 console.log(str) 에 대한 출력 결과를 보면 동기식으로 코드가 실행 되었음을 알 수 있다.
내 생각
비동기 동기에 대한 개념이 조금 더 잡혔다. 좀 더 완벽히 정리 해봐야 할 필요가 있을 것 같다. 추가적으로 Blocking , non-Blocking 을 정리해 봐야겠다.
동기 : 작업에 대한 주체? 컨트롤? 이 나에게 있는 것
비동기 : 작업에 대한 주체를 다른 매개체(Callback 함수)에게 맡기는 것
이라고 간단하게 정리를 하겠음
Promise, async/await 도 키워드는 알고 있었으나 정확히 어떻게 사용되는 지 그리고 구체적으로 정확히 어떻게 되는지는 아직 모르겠다. 일단 간단하게 이해하고 나중에 살을 붙여야겠음....
멀고도 험한 개발의 세계...
[참조] :
https://jindev-t.tistory.com/90
https://medium.com/@vivianyim/synchronous-vs-asynchronous-javascript-de4918e8ad62
https://springfall.cc/article/2022-11/easy-promise-async-await
'CS 지식' 카테고리의 다른 글
서킷 브레이커(Circuit Breaker)란? (0) | 2023.09.25 |
---|---|
컴퓨터 구조란? (0) | 2023.08.09 |
트레이싱(Tracing)이란? (0) | 2023.07.19 |
DB Clustering이란? (0) | 2023.07.18 |
클러스터링이란? (0) | 2023.07.18 |