동기와 비동기
자바스크립트는 기본적으로 동기적이라고 할 수 있다.
코드 블럭들은 호이스팅* 이후에 바로 실행되고 코드 한 줄이 끝난 후에야 다음 줄이 실행될 수 있다는 뜻이다.
*호이스팅: var, function declaration
예시 1)
function printImmediately(print){
print();
}
printImmediately(() => console.log('hello'));
하지만 항상 코드가 동기식으로 작성되면 문제가 발생한다.
네트워크, 서버 통신을 하는 동안 해당 줄에서 코드 실행이 멈추게 되기 때문이다.
웹의 경우 웹 화면이 멈추게 되는데 이러한 문제를 방지하기 위해 "비동기" api를 사용하게 된다.
setTimeout은 대표적인 비동기 api 중 하나이다.
(이 포스팅은 ajax 통신 등의 서버 통신을 대신하여 간편하게 setTimeout api를 사용하여 비동기에 대해 설명한다.)
setTimeout(() => {
console.log('2');
}, 1000);
function prindWithDelay(print, timeout){
setTimeout(print, timeout); // browser-api
}
prindWithDelay(()=> console.log('asynch callback'), 2000)
function printImmediately(print){
print();
}
printImmediately(() => console.log('hello'));
위의 코드를 실행해보면 asynch callback hello로 출력되는 것이 아니라
hello asynch callback으로 출력됨을 확인할 수 있다.
이렇게 한 줄의 실행이 끝날 때까지 기다리지 않고 다음 코드를 먼저 실행하는 것이 비동기라고 할 수 있다.
하지만 비동기로 처리하면 효율적이긴 하나 비동기 코드가 끝날 시점을 명확하게 알지 못하기 때문에
개발하면서 코드 실행 순서를 알기 어렵다는 단점이 있다.
따라서 이를 보완하기 위해 Callback, Promise, asynch, await 등이 쓰인다.
Callback의 사용법과 Callback 지옥
callback은 이미 map, filter, sort 등 배열 api에서 많이 다루었다.
그러나 모든 callback이 비동기인 것은 아니다.
callback은 작성될 때 실행되는 것이 아닌 후에 다른 함수에 의해 호출되는 모든 함수를 말하는 것이기 때문에 동기식일 수도 비동기일 수도 있다.
function printImmediately(print){
print();
}
printImmediately(() => console.log('hello'));
printImmediately()의 매개변수인 () => console.log('hello')도 callback 함수이지만 동기식이다.
그러나 아래의 경우를 보자
function prindWithDelay(print, timeout){
setTimeout(print, timeout); // browser-api
}
prindWithDelay(()=> console.log('asynch callback'), 2000)
print에 해당하는 () => console.log('asynch callback') 함수는 2초를 기다려서 실행되는
asynch callback이라고 할 수 있다.
이러한 callback 함수는 유용하긴 하나 nesting된 경우가 많을 때,
굉장히 복잡한 코드를 가지게 되어 가독성이 매우 떨어진다.
아래는 그 예이다.
아래와 같은 클래스가 있다고 생각해보자.
class UserStorage{
loginUser(id, password, onSuccess, onError){
setTimeout(()=> {
if(
(id === 'ellie' && password === 'dream') ||
(id === 'coder' && password === 'academy')
){
onSuccess(id);
}else{
onError(new Error('not found'))
}
}, 2000)
}
getRoles(user, onSuccess, onError){
setTimeout(() => {
if( user === 'ellie'){
onSuccess({name: 'ellie', role: 'admin '});
}else{
onError(new Error('no access'));
}
}, 1000);
}
}
1. ID와 Password 입력받기
2. 로그인하기
3. 역할 요청
4. 출력
하는 코드를 작성해보면 이렇게 된다.
소위 Callback chain이라고 불린다.
const id = prompt('enter your id');
const password = prompt('enter your password')
userstorage.loginUser(
id,
password,
user => {
userstorage.getRoles(user,
(userWithRole) =>{
alert(`Hello ${userWithRole.name}, you have a ${userWithRole.role} role`)
},
error => {
console.log(error)
} )
},
error =>{
console.log(error)
}
)
물론 callback 함수에 이름을 붙여서 최대한 간결하게 해보려고는 할 수 있다.
하지만 callback 함수가 또다른 callback함수를 그리고 또 다른 callback 함수를 호출하는 것은 같기 때문에
비지니스 로직을 한눈에 파악하는 것도 어렵고 코드에 문제가 생겼을 때 디버깅하기도 복잡한 단점이 있다.
const userstorage = new UserStorage();
const deliverID = (id) => userstorage.getRoles(id, printIdRole, printError)
const printIdRole = (userWithRole) => {
alert(`Hello ${userWithRole.name}, you have a ${userWithRole.role} role`)
}
const printError = (error) => console.log(error)
userstorage.loginUser('ellie', 'dream', deliverID, printError)
따라서 이러한 단점을 보완하기 위해 Promise와 await, asynch 같은 개념이 등장했다.
'javascript' 카테고리의 다른 글
[함수형 언어, 자바스크립트] 비동기 처리 (3) await과 async의 이해 (0) | 2020.11.19 |
---|---|
[함수형 언어, 자바스크립트] 비동기 처리 (2) Promise의 이해 (0) | 2020.11.17 |
[함수형 언어, 자바스크립트] 배열에 함수 체이닝 (1) (0) | 2020.10.22 |
[함수형 언어, 자바스크립트] 배열 (0) | 2020.10.22 |
함수형 언어, 자바스크립트 (0) | 2020.10.14 |