- 비동기 처리? (공부 사이트) :
웹 서핑을 하다보면, 예를 들어, 유튜브를 보다보면, 재생버튼을 누르고=> 로딩이 길어지는 경우 =>
귀찮아서 다시 다른 화면으로 넘어가고자, 특정한 버튼을 누르고 넘어가는 경우가 있다. 그러나, 자바스크립트는
싱글스레드 언어이고, 동기적 프로그래밍 언어이다. 즉, 비동기적 처리를 본래 할 수 없는 언어이다.
하지만, 그러한 동기적 프로그래밍 언어에도 위와 같은 상황을 가능하게 해주기 위해서는(동영상이 로딩 중일 때 다른 버튼이 클릭조차 안된다면???..)
비동기적 처리를 할 수 있어야한다. 따라서, 자바스크립트 엔진에서는 동기적 처리를 하는 메모리힙과 콜스택에 더하여(사실상 웹 브라우저가 자바스크립트 +a이기 때문에 가능한 처리임)
Web APIs, callback queue, event loop를 가지고 있다.
예를 들어, setTimeout, DOM 등이 web API에 해당한다. 이 때, setTimeout을 예로들면, 코드에 setTimeout이 있다면,
setTimeout이 pop되면서 webAPIs로 가게된다(이 때는 타이머와 매개변수로 넣어준 함수의 형태).
그리고 시간이되면 web API가 해당 함수를 콜백 큐에 넣어주고, 콜스택과 콜백큐 사이의 이벤트 루프가 콜 스택의 빈자리를 체크하며, 콜백 큐 속의 함수를 콜 스택으로 넣어주고 실행하는 방식이다.
이 때, setTimeout에 정해준 시간대로 뭐랄까.. 딱딱? 맞게 실행이 된다고 생각되지만, 실제로는 call stack의 눈치를 많이 본다라고 할 수 있다.
즉, 콜 스택에 실행할 것이 남아있다면, 아무리 시간이 됐어도 바로 callback큐를 통해 함수를 call stack에 푸시해줄 수는 없다는 것이다.
실제로, setTimeout(()=>{console.log('me first!')},0)의 형태로 써준 다음 줄에 console.log('hi');를 해주면 setTimeout의 시간이 0밀리초이기 때문에 콘솔창에 'me first!'가 먼저 출력되고, 'hi'가 출력될 것 같지만,
실제로는 hi가 먼저 출력되고 => me first가 출력된다. 결론적으로, call stack이 좀 더 우선권을 가진다고 볼 수 있다(어쨌든, 메인은 콜스택이니까).
그리고 이러한 처리(앞에서 말했던 setTimeout, DOM 이벤트 처리 등등)를 위해서, 콜백함수, Promise & then & catch 그리고 async & await 등이 나온 것이고(순서대로), 이번 스프린트는 그에 대해 배워보는 스프린트였다.
- About Promise :
스프린트 기간동안 가장 어려웠던 개념인 Promise에 대해서 정리해보고자 한다.
먼저, Promise는 하나의 Object, 즉, 객체이다. 무슨 객체인가하면, 특정한 실행을 약속하는 객체이다(?).
추상적이지만, 이것이 Promise를 나름 잘 설명한 정의라고 생각한다. promise는 promise가 나오기전에
callback함수를 계속해서 불러서 (callback hell에 빠질 수 있는) 다음 실행을 약속한 것과 달리, 좀 더
심플한 코드로(콜백헬 안녕!) 다음 실행을 약속할 수 있는 방법이라고 할 수 있다.
이 때, Promise는 앞에서 말했듯이, 이행을 약속해주는 객체인데, 만약 정상적으로 약속을 이행할 수 있는 상태라면, resolve를 실행한다.
하지만, 어쩔 수 없는 상황(에러)으로 인해 약속 이행이 불가능해지면, reject를 실행한다.
(그리고 이러한 세가지 상태들이 Promise의 3가지 상태인데 순서대로, pending(이행 대기), fulfilled(이행), rejected(거절)이다)
즉, Promise는 이처럼 두개의 파라미터(resolve, reject)를 인자로 받는다. 그리고 여기에는 함수를 넣어줄 수 있다.
이 때, 이전의 콜백함수에서 문제가 되었던 콜백헬이 Promise에서는 어떻게 구현되는지를 보면,
여기서 then이라는 Promise.prototype의 메서드가 나온다. then은 약속과 약속을 이어주는 역할을 한다고 생각해볼 수 있다.
즉, Promise의 상태가 fulfilled or rejected된 이후 즉, 대기 상태에서 이행을 하던, 거절을 하던 실행을 한 뒤에 다음 약속이 있다면,
그 약속으로 바로 이어주는, 말그대로 then : ~ 다음에(and then~ 으로 자주쓰는 말처럼)는 이 약속으로! 라는 역할을 해준다. 그리고 이러한 then은 매개변수로 함수를 받는데,
만약 이전의 Promise(then과 연결된)의 상태가 fulfilled였으면 첫 번 째 매개변수의 함수를 실행하고(함수에서 매개변수로 fulfilled Promise에서 resolve한 값을 받아올 수 있음),
rejected였으면, 두 번 째 매개변수에 있는 함수를 실행한다.
(역시, 이전 Promise에서 reject한 값을 매개변수로 받아올 수 있음)
이 때, catch(Promise.prototype의 메서드)를 then 체인(?)의 마지막에 써주면, 모든 로직에서의 에러를 마지막 catch하나로 처리해 줄 수 있는 간편함이 있다.
그러나, 이러한 Promise도 사실상 Promise 체이닝에 있어서
콜백 지옥과 같은 형상을 띌 수 있기 때문에 이제는 async & await로 넘어가자(ES8 문법)
- About async & await :
Promise와 마찬가지로 async와 await, 사실상 코드를 쓸 때 직접적으로 쓰이는건 await뿐이지만, 어쨌든!
둘 다 단어를 통해 쓰임새를 이해하기 쉽게 만들어졌다.
먼저, await를 쓰기 위해서는 함수명(화살표 함수의 경우는 async ()=>{} 이런식으로)앞에 async를 써줘야한다.
그러면 await는? 앞의 Promise에서는 약속하나를 이행하고 난 뒤에 then 다음 약속, 이런식으로 then을 써서 이행을 연결해줬는데,
사실 그 방법도, 즉, promise chaning도 그 체인이 길어지면 감당하기 callback 헬처럼 될 수 있는 문제점이 있었다.
따라서, 좀 더 직관적으로 보기 좋은 코드를 만들기 위해서?
await를 썼다. 사실 여태까지 모든 비동기 처리의 포인트는 앞의 것을 하기전까지 기다렸다가, 그것이 끝나면, 그 다음처리를 해줘와 같은..비유해보면 바톤 터치, 꼬리물기와 같다.
그리고 async에서는 await가 그 역할을 한다.
즉, Promise에서는 then을 통해 계속 연결되는 체인 형태로 만들었었는데, async에서는 await로 개별 약속을 이행하고, 리턴값을 받고,
그 다음 줄에 또 await로 개별 약속을 처리하고, 리턴값을 받고
이런식으로 Promise와 달리 코드가 계속 연결된 형태가 아닌
동기적 프로그래밍 코드와 같이 따로따로 쓰여진 형태가 되어 직관적으로 파악하기가 좋다.
이 때, await의 뒤에 오는 함수 혹은 로직은 Promise를 포함하고 있어야한다. 달리 말해, await는 일반 함수와 같이 쓰이면 효력이 없다.
단지, Promise를 쓰되 then을 쓰지 않고, 좀 더 직관적으로 보기 좋은 코드를 위해서 쓰이는 것이라고 이해하면 될 듯하다.
- fetch API?( 참고 사이트 ) :
비동기 작업을 해줘야하는 또다른 작업으로 네트워크 요청, 즉, 특정한 서버에 request를 하고 response를 받아오는 작업을 생각해볼 수 있다.
만약, 네트워크 요청이 오래걸린다면, 그 요청동안 다른 동기적인 처리를 할 수 있어야하기에 비동기적 처리가 필요하다.
그리고 이 때, 쓰는 API로 fetch API가 있다(node.js에는 없음).
기본적인 사용법? 을 말해보면, 먼저, fetch(URL)을 해주면 해당 URL에 있는 정보를 받아올 약속 이행 문서와 같은 Promise를 객체로 리턴받는다.
그리고 이 Promise객체는 내부적으로 fulfilled 상태가 되면 response를 리턴한다.
그러면, then을 이용한 체이닝을 통해 response에 있는 json()메서드를 이용해 해당 URL에 있는 자료를 받아올 수 있다