
비동기적이란, 요청을 보낸 후 응답과 관계 없이 다음 동작을 실행할 수 있는 방식을 의미한다. JS는 싱글 쓰레드이기 때문에 성능을 향상하기 위해서 비동기적 코드를 지원하는데 이를 알아보자. 먼저 Event Loop에 대해서 먼저 알아야 한다.

JS의 기본 구조이다. 메모리를 뜻하는 heap 그리고 우리가 집중할 호출 스택 (stack) 이 존재한다.

JS가 코드를 저장하는 순서는 위와 같다. 위에서 부터 아래로 즉 multiply(a,b) > square(n) > printSquare(n) > printSquare(4); 순으로 실행된다.

이렇게 순차적으로 진행될때 만약 데이터를 받아오는데 오래 걸리는 작업이 존재하면 어떻게 될까 ?
아래 작업에 차질이 생기게 된다. 그래서 우린 다른 방법을 찾아야 한다.

위의 그림의 실제 콘솔은
Hi
JSConfEU
there
이다. 이를 설명해보겠다.
- HI를 stack에서 받고 호출하여 콘솔에 입력
- setTimeout이 stack에서 나오면서 안에 있는 cb function 이 timer에 감싸져서 webapis로 보내진다.
- JSConfEU를 stack에서 받고 호출하여 콘솔에 입력
- cb가 나오면서 there 콘솔에 입력
따라서 우린 비동기식 이라는 방식을 한번 맛보게 된거다.

브라우저에서 사용자에게 레스터 이미지를 보여주기 위해 html, css, javascript 코드를 변환하는 과정
여기서 render queue(FIFO)를 생각하며 비동기식이 어떤 효과를 내는지 생각해보겠다. render queue는 call stack이 비어져있어야만 작동한다.
만약 동기식이라면(call stack이 끝날때까지 비어지지 않음) 그 동안 작동하지 못한다는 이야기 이다.
scroll event를 생각해보면 내릴때마다 수 많은 event가 생기는데 이를 전부 동기식으로 처리하게 되면 모든 event가 끝날때까지 render queue가 작동하지 않는다. 이때 비동기식으로 처리하여 호출 스택이 비어있는 순간을 만든다면 render queue가 작동되어 효율적으로 브라우저를 작동시킬 수 있다.
어떤 event가 task queue에서 call back into(다시 돌아가다) program하여 event가 실행되기 때문에 call back이라고 한다.
위에 적었던 것처럼 call back은 browser에서 Web Apis로 function body를 보낼 수도 있다.
예외로 .forEach는 call back function을 받지만 내부적으로 배열을 돌며 비동기적으로 되지 않고 동기식으로 작동하게 된다.
함수 안에 argument로써 쓰이는 함수인데 여기서 문제가 발생한다. 콜백함수를 넣는데에 여러가지 변수가 존재한다는 것이다. 가장 대표적인 것으로는 콜백 함수가 호출되지 않았을때이다.
결제 시스템 같은 민감한 시스템에서 콜백 함수가 호출되지 않으면 해당 이벤트를 끉는 검사가 존재해야 한다.
이를 위해서 생성된게 reject를 지원하는 Promise이다.
const getData = function() {
// go fetch data from some API...
// clean it up a bit and return it as an object:
return data
}
const myData = getData()
const pieceOfData = myData['whatever']
위의 예시에서 data를 fetch하는데 오래 걸린다면 아래 pieceOfData는 undefined의 인덱스를 탐색하게 되어 오류가 나오게 된다.
이때 사용하게 되는 것이 Promise이다.
영어로 약속을 뜻하는 Promise는 말 그대로 미래의 특정 시점에서 value를 생산할 수 있는 객체이다.
const myData = getData() // if this is refactored to return a Promise...
myData.then(function(data){ // .then() tells it to wait until the promise is resolved
const pieceOfData = data['whatever'] // and THEN run the function inside
})
이제 myData가 resolve(함수가 작동)될 때까지 기다렷다가 pieceOfData변수가 넣게 된다. 자세한 사항을 더 살펴보자.
var p = new Promise(function(resolve, reject) {
// Do an async task async task and then...
if(/* good condition */) {
resolve('Success!');
}
else {
reject('Failure!');
}
});
p.then(function(result) {
/* do something with the result */
}).catch(function() {
/* error :( */
}).finally(function() {
/* executes regardless or success for failure */
});
위의 p는 Promise로 선언되었다. 위의 코드로 then, catch, finally를 설명해보겠다.
Promise가 resolve(해결)을 했다고 가정하고 실행하는 function을 받는 method이다. 안의 parameter는 Promise내부에서 선언한 resolve의 parameter가 들어가게 된다. 위의 코드에서는 'Success!' 이다.
then과 반대로 reject에 대해서 then처럼 작동하는 method이다.
then이든 catch든 기본으로 작동하는 method이다.
모든 Callback이 resolved되었을 때 실행, 하나라도 reject면 catch이다.
모든 Callback이 resolved되었을 때 실행. 하나라도 reject면 catch
(result에는 해당 parameter들이 Object형태로 들어 있다.)
Promise.all([promise1, promise2]).then(function(results) {
// Both promises resolved
})
.catch(function(error) {
// One or more promises was rejected
});
multiple AJAX request를 한번에 실행시킬 수 있다.
var request1 = fetch('/users.json');
var request2 = fetch('/articles.json');
Promise.all([request1, request2]).then(function(results) {
// Both promises done!
});
하나만 reject일 때 그 reject를 만나자마자 catch를 뱉는다.
var req1 = new Promise(function(resolve, reject) {
// A mock async action using setTimeout
setTimeout(function() { resolve('First!'); }, 4000);
});
var req2 = new Promise(function(resolve, reject) {
// A mock async action using setTimeout
setTimeout(function() { reject('Second!'); }, 3000);
});
Promise.all([req1, req2]).then(function(results) {
console.log('Then: ', results);
}).catch(function(err) {
console.log('Catch: ', err);
});
// From the console:
// Catch: Second!
promise array 끼리 경쟁 붙힌다. 먼저 된게 들어가게 된다.
var req1 = new Promise(function(resolve, reject) {
// A mock async action using setTimeout
setTimeout(function() { resolve('First!'); }, 8000);
});
var req2 = new Promise(function(resolve, reject) {
// A mock async action using setTimeout
setTimeout(function() { resolve('Second!'); }, 3000);
});
Promise.race([req1, req2]).then(function(one) {
console.log('Then: ', one);
}).catch(function(one, two) {
console.log('Catch: ', one);
});
// From the console:
// Then: Second!
이 편리한 Promise 기술은 흔히 Callback hell이라 불리는 function이 끝없이 꼬리를 무는 현상은 해결하였지만 여전히 코드를 길게 만드는 현상을 만들어냈다. 이를 해결하기 위해서 더 간편하게 할 수 있는 async/ await 가 존재한다. 이는 2편에서 진행하도록 하겠다.