콜백 함수엔 다양한 함수들이 있다.
SetTimeout을 이용한 일정시간 뒤의 log출력
forEach를 이용한 배열 출력
와 같은 다른코드의 인자로 넘겨주는 함수를 콜백함수라고 한다.
콜백함수의 경우는 제어권이 원래있던 코드가 아닌 콜백함수에 넘겨준 주체에있다고 생각해야한다.
SetTimeout의 경우 1초후 hello라는 로직을 처리하는 제어은 callback함수인 setTimeout에게 있다.
콜백함수의 제어권에 대해
● 1.함수 호출시점에 대한 제어권
콜백함수의 제어권에 대해서 이야기하기 위해 setInterval함수를 사용해 예를 들어보겠다.
위와 같은 timer 함수를 만들었다면 0.3초에 1씩 올라가는 호출 시점을 가지는 것은 setInterval이다.cbFunc를 그냥 호출하면 0만 호출되고 만다. 하지만
다음과 같이 timer를 설정해주면 호출 제어권을 setInterval이 가지게 되어서 setInterval이 실행된다.
이로써 callback함수에서 함수는 호출시점에 대한 제어권을 가지는 것을 알 수있다.
●2. callback함수는 인자에 대한 제어권을 갖는다.
map함수를 이용해서 예를 들어보겠다.
다음과 같은 함수를 호출하게 되면 newArr을 호출하는 부분은 undefined가 호출된다. map함수를 사용하면 무슨 값이든 return을 해주어야 newArr을 채울 수 있기 때문이다.
와 같은 return을 해주면 newArr의 log를 찍었을때 현재값인 10,20,30에 5씩이 더해진 값이 log로 찍혀서 나오는 것을 확인할 수 있다.
위의 방법을 적용해서
다음과 같이 index, currentValue의 위치를 바꿔서 적용되면 newArr에 기존 index의 값인 0,1,2에 각각 5가 더해진 5,6,7이 newArr에 배열된 것을 확인 할 수 있다. 위의 방법을 통해 콜백함수를 적용할 때 제어권은 콜백함수 그 자체가 들고 있는 것을 알 수 있다.
●콜백함수에서의 this
콜백함수도 함수이기때문에 this가 전역객체를 참조한다.
하지만 제어권을 넘겨받을 코드에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그대상을 참조한다.
addEventListener메서드의 경우 부모를 상속한다
document.body.querySelector('#a').addEventListener의 경우 document.body.querySelector('#a')가 부모가 되고 this는 그부모를 참조한다.
map함수의 매커니즘을 직접 제작하면서 this의 역할을 확인할 수 있다. Array.prototype내의 this는 앞에있는 Array의 객체를 가르키는 것을 볼 수 있다. 또한 call/apply를 통해 직접적으로 함수내의 메서드를 명시할 경우 this를 명시적으로 binding하기 때문에 this내의 다른 값을 담을 수 있다.
요약하자면 제어권을 넘겨받을 코드에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조한다.
forEach에 this
forEach, map, filter 등도 콜백함수이다.
위와 같은 코드에서 forEach는 obj.logValues를 직접적으로 binding하기때문에 this값이 global이나오면 안된다. 하지만 위의 값의 결과는 this가 글로벌 입니다를 3번띄우는 것이다.
함수에 this를 쉽게 bind하는법
그렇다면 함수에 어떻게 쉽게 this 를 binding을 하냐라고 묻는다면
위의 결과처럼 bind를 사용한다면 쉽게 콜백 함수에 접근해서 bind를 할 수 있다.
콜백지옥이란
콜백지옥이란 익명 함수로 전달되는 과정 (콜백함수)를 너무 많이 사용되어서 들여쓰기가 너무 되어서 가독성이 어려워, 수정 및 유지 보수가 어려워 지는 코드를 의미한다.
프로그래머로써 하면 안되는 코드이다.
콜백지옥을 해결하는 방법에 대해서 이야기 하기 위해선 동기(sync)와 비동기(async)에 대해 알아야한다.
동기, 비동기
동기 : 현재 실행중인 코드가 끝나야 다음 코드를 실행하는 방식
비동기 : 실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어가는 방식이다. ex) settimeout, addEventListener..
다음과 같은 코드가 있으면 먼저 실행되어야 할 부분은 setTimeout이 실행되고 뒤에 여기좀 봐주세요의 그냥 로그가 찍혀야하지만 결과 값은 반대다. 위와 같은 결과처럼 요청, 실행 대기, 보류 등과 관련된 코드는 모두 비동기적 코드이다.
비동기적 코드가 많으면 문제가 되는 이유
하나의 서버로만 구현이 되면 코드를 짜는데 어려움이 없을 수 있지만, 서버가 두개가 된다면 서버의 시간 차이로 비동기적 코드에 오류가 생길 수 있다. 그렇게 하기 위해서 비동기 작업에 동기적 표현을 하다보니 콜백지옥처럼 코드를 짜게 된다.
비동기 작업을 동기적 표현으로 바꾸기 위해서 자바스크립트는 Promise, Generator, async/ await 등의 방법으로 해결 할 수 있다.
Promise에 대해
Promise는 비동기 처리에 대해, 처리가 끝나면 알려달라는 '약속'을 거는 것이다.
- new 연산자로 호출한 Promise의 인자로 넘어가는 콜백은 바로 실행된다.
- 내부의 resolve 함수를 호출하는 구문이 있을 경우 resolve 둘 중 하나가 실행되기 전까지는 다음, 오류로 넘어가지 않음
- 비동기작업이 완료될 때 비로소 resolve,reject를 호출한다.
위의 구문은 Promise의 기본적인 세팅이다. resolve를 함수로 가지고 있고, 작업뒤에 resolve를 호출 해주어야한다. resolve 호출을 받을때에는 Promise함수뒤에 then을 붙여주고 return 하여 다시 Promise를 만들어 추가적으로 적용해야할 구문을 호출해주는 식으로 Promise함수를 세팅하면 된다.
Promise의 리팩토링
리팩토링이란 비효율적인 코드를 효율적으로 바꿔주는 것이다.
계속 promise를 추가 해주기 보다는 위의 방법처럼 처음 시작할 때는 addCoffe("아메리카노")뒤에 ()를 붙여줘야 두번째 return인 new Promise(function(resolve)까지 접근 하는 것이기 때문에 처음 구문에서는 괄호를 붙여주어야한다. 그다음부터는 .then으로 쉽게 뒷 구문을 추가하면 된다.
Generator에 대해
Generator문법은 반복할 수 있는 Iterator 객체를 생성한다라고 설명 할 수 있다. Iterator 객체는 스스로를 반환하여 반복하는 next 객체를 가지고 있다.
즉, 비동기 작업이 완료되는 시점마다 next 메서드를 호출해주면 Generator 함수 내부소스가 위->아래 순차적으로 진행된다.
위의 코드에서 coffeGenerator = function* 에 function*부분이 제너레이터다. 제너레이터가 실행되면 yield 를 만날시 멈추고, 뒤에 구문이 끝날때 까지 기다리고 다시 next 메소드를 호출하면 함수가 yield를 만날때까지 진행되다가 yield 구문을 만나면 멈추고를 반복한다. stop을 걸어준다고 생각하면 편하다.
Promise를 이용한 async/await
코드를 보면서 async/await에 대해 이야기해보자면 async는 함수앞에 붙여서 시용하며, await이 실행되는 함수는 항상 Promise를 반환해야한다. Promise를 반환한다면 해당 함수가 진행 될 때까지 기다린다.
오늘은 이렇게 12일차 javascript 문법에대해 공부를 하였다.
팀프로젝트 코드도 같이 짜면서 더 공부해야겠다.
'코딩 > Javascript' 카테고리의 다른 글
TIL 14일차 (페이지네이션, 비밀번호조건 추가) (0) | 2024.05.08 |
---|---|
TIL 13일차(사이드바를 이용한 UI적용) (0) | 2024.05.07 |
TIL 11일차 -1 (코딩테스트, this에 대한 이해) (0) | 2024.05.01 |
TIL 10일차 (영화 검색시 결과 값 나오게하기) (1) | 2024.04.26 |
TIL 9일차 (개인프로젝트 OpenAPI이용해서 영화 검색사이트제작) (1) | 2024.04.25 |