header

Day 12 - 2022/11/01

💡 새롭게 알게된 점

JS에서의 모듈화와 비동기 처리

Module

각 컴포넌트에 대한 Module화 후 export/import 하는 것의 장점이 무엇인가?

각 js 별로 사용되는 모듈을 명시적으로 import 하기 때문에 사용되거나 사용되지 않는 스크립트를 추적할 수 있다.

script 태그로 로딩하는 경우 불러오는 순서가 중요하지만, import로 불러오는 경우 순서는 무관하다.

script src로 불러오는 것과 달리 전역오염이 일어나지 않는다.

import를 사용하려면..

웹 서버가 필요하다. 로컬 웹 서버로도 가능하다.


Callback

비동기 처리란?

특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 처리 방식이다.

비동기 처리의 예시

addEventListener

setTimeout, setInterval

XMLHttpRequest(XHR)


Promise

Promise는 비동기 작업을 제어하기 위해 나온 개념으로, 콜백 지옥에서 어느정도 벗어날 수 있게 해준다.

Promise로 정의된 작업끼리는 연결(Chaining)할 수 있으며, 이를 통해 코드의 깊이가 크게 증가하지 않는 효과가 있다.

Promise 사용하기

const promise = new Promise((resolve, reject) => {
    {...}
});

Promise 내부에서 비동기 상황이 종료될 때 resolve 함수가 호출되고, 오류 상황일 때 reject 함수가 호출된다.

function asyncPromiseWork() {
    {...}
    return new Promise((resolve, reject) => {
        {...}
        return resolve('complete');
    });
}
   
asyncPromiseWork().then(result => console.log(result));

Promise에서는 then을 이용해 비동기 작업 이후 실행할 작업을 지정한다.

위 코드에서 then의 result에는 resolve를 호출하며 넘긴 ‘complete’가 들어있다.

promiseWork()
    .then(result => {
    	   return promiseNextWork(result);
    }).then(result => {
        return promiseThirdWork(result);
    }).then(result => {
    	   return promiseFinalWork(result);
    });

Promise의 then 내에서 promise를 return하여 chaining으로 연결할 수 있다.

콜백보다는 제어하기 쉽고, 보기가 편하다는 장점이 있다.

promiseWork()
    .then(result => {
    	   return promiseNextWork(result);
    }).then(result => {
   	   return promiseThirdWork(result);
    }).then(result => {
   	   return promiseFinalWork(result);
    }).catch(e => {
   	   alert('에러 발생..');
    });

Promise chaining 중 작업이 실패할 경우 catch를 통해 에러 핸들링이 가능하다.

promiseWork()
    .then(result => {
    	   return promiseNextWork(result);
    }).then(result => {
    	   return promiseFinalWork(result);
    }).catch(e => {
    	   alert('에러 발생..');
    }).finally(() => {
    	   alert('작업 완료');
    });

Promise 중 성공과 실패의 여부에 관계없이 호출해야하는 코드가 존재한다면 finally에서 처리한다. 자원 정리나, 로딩 중 처리를 끊는 작업 같은 경우에 사용된다.

Promise의 내장 함수

Promise.all(iterable)

const promise1 = delay(1000);
const promise2 = delay(2000);
const promise3 = delay(3000);
   
Promise.all([promise1, promise2, promise3]).then(() => {
    // promise1, promise2, promise3이 모두 처리된 이후 호출
});

여러 Promise를 동시에 처리(병렬)할 때 유용하다.

Promise.race(iterable)

여러 Promise중 하나라도 resolve 혹은 reject 되면 종료된다.

Promise.any(iterable)

Promise.race(iterable)과 유사하지만, 여러 promise중 하나라도 resolve 되면 종료된다.

Promise.allSettled(iterable)

여러 promise들이 성공했거나 실패했거나의 여부와 관계없이 모두 이행된 경우를 처리할 수 있다.

Promise.resolve()

주어진 값으로 이행하는 Promise.then 객체를 만들고 주어진 값이 Promise인 경우 해당 Promise가 반환된다.

Promise를 반환해야 하는 코드에서, 상황에 따라 비동기 처리를 하지 않고 값만 내보내는 경우 해당 함수를 사용하는 곳에서 반환 형식이 Promise일 수도 있고 아닐수도 있기 때문에 그런 상황에 대비하여 반환 형식을 Promise로 맞춰주기 위해 사용된다.

Promise.reject()

주어진 값으로 reject 처리된 Promise.then 객체를 만들고 주어진 값이 Promise인 경우 해당 Promise가 반환된다.

강제로 reject해야 하는 경우에 사용할 수 있지만, 많이 사용하지는 않는다.


async, await

Promise가 chaining 형태로 사용하기 때문에 1-depth여서 callback보다는 좋지만, 그래도 여전히 코드의 흐름과 실제 실행 순서가 일치하지 않기 때문에 코드의 가독성을 어렵게 만드는 문제가 존재한다.

따라서 async, await를 사용하면 Promise를 비동기로 실행이 되지만, 동기 코드처럼 보이게 짤 수 있다.

async 키워드 함수가 붙은 함수는 실행 결과를 Promise로 감싼다.

async function asyncRun() {
    return 'hello';
}
   
console.log(asyncRun()); // > Promise {<fulfilled>: 'hello'}
asyncRun().then((msg) => console.log(msg)); // hello 

기본적으로 await는 async로 감싸진 함수 scope에서만 사용 가능했지만, top level await가 등장하여 top level에서도 사용 가능하다. (브라우저에 따라 다름)



❗️ 느낀점

callback, promise, async/await에 대해 다시 한번 자세히 공부할 수 있던 계기였다.

promise에 이만큼 다양한 함수가 존재한다는 것을 처음 알게되었고, 나중에 기회가 된다면 race나 all 같은 재밌어 보이는 함수를 사용해 봐야겠다.

오늘도 고생했다👊