[JS] 비동기와 Promise에 대해 알아보자

·2024년 3월 11일

Javascript

목록 보기
13/17

콜백함수

파라미터로 전달되는 함수! 함수 안에 호출되는 다른 함수라고 생각하면 된다!

console.log((a,b)=>{a+b}(3,2));

보다시피 콜백함수는 일회성으로 사용이 되는데,
그렇기 때문에 익명함수로 사용한다.
한 번 쓰는거 괜히 이름을 지어주다가는 다른 함수명들과 이름이 겹칠 수 있기 때문

동기

작성한 코드를 순서대로 실행하는 것
즉, 요청 결과 응답 후, 다음 동작이 실행되는 방식

function fetchData(studentId){
  const student = {
    id: studentId
  }
  console.log(student); //{id:1}
}
fetchData(1);

위 student의 값이 할당 된 후, console.log가 수행되므로,
fetchData(1) 실행 시, 의도한 대로 {id:1}이 뜬다.

비동기 (asynchoronous)

요청 결과 응답과는 별개로 다른 동작이 실행되는 방식,
쉽게 말하면, '기존 함수를 제껴두고 계속 다음 동작이 실행되는 방식'임.

대표적으로 setTimeOut(function,time)은 비동기함수임. (time만큼 시간이지날 경우 function실행)

function fetchData2(studentId){
            let student;
            // setTimeout(function, time)
            setTimeout(function() {
                student = {id: studentId}
            }, 3);
            console.log(student);
        }
        fetchData2(1); //undefined

이 코드는 undefined가 출력된다.
3초가 지났을 때 student 이름에 객체를 할당하려고 했는데, 이미 console.log가 3초 전에 호출되었기 때문이다.

하지만 매개변수에 함수를 넣으면 어떻게 될까?

function fetchData3(studentId, callback){
            let student;
            
            setTimeout(function() {
                student = {id: studentId}
                callback(student);
            }, 3000);
        }
fetchData3(1, (student) => {
            console.log(student); //3초 후, {id:1} 출력
        });

다음 코드는 놀랍게도 undefined가 뜨지 않는다!
위 코드와 아래 코드의 차이는, '콜백 함수'를 사용했다는 것.
콜백 함수를 실행했을 때, 비동기 함수를 실행했을 경우 원하는 결과를 좀 더 쉽게 받아볼 수 있다.
다시 말해, 콜백함수를 이용하면 원하는 시점에 함수를 호출할 수 있으므로, 비동기 문제를 해결할 수 있다.

콜백 지옥

이렇듯 비동기를 컨트롤하기 위해 콜백 함수를 많이 쓰는데,
매개변수로 잇달아 함수를 호출하게 된다면, 보기에도 상당히 안좋고, 가독성도 떨어지게 된다.
이를 콜백지옥이라고 한다.

Promise

그래서 아예 비동기 전용으로 실제 정상 기능이 수행된 상태의 데이터가 담긴 객체를 만들어 사용하기로했다. 객체의 생성은 Promise 키워드로 한다.

콜백함수와의 차이

  1. 비동기의 목적성을 명확히 보여줄 수 있음
  2. Promise가 제공하는 콜백 인자인 resolve,reject를 통해 상태처리가 유용함
  3. then,catch를 이용해 코드 가독성을 높일 수 있다.

Promise의 상태

Promise는 두가지의 상태를 가진다. (엄밀히는 pending,fulfilled,rejected 3가지)
1. 데이터를 받아온 상태 (pending)
2. 데이터를 받아온 후 -> 성공시 resolve 호출(fulfilled), 실패시 reject(rejected) 호출

 let firstPromise = new Promise((resolve, reject)=>{
            setTimeout(()=>{
                resolve('data');
            },2000);
        });
        console.log(firstPromise);

2초 전
2초 후
가 출력된 모습을 볼 수 있다.

소비자 함수

이제 promise가 생성한 데이터를 소비해야한다.
then, catch 이 두개가 promise의 데이터를 소비할 수 있는 함수다.

then

let firstPromise = new Promise((resolve, reject)=>{
            setTimeout(()=>{
                resolve('data');
            },2000);
        });
        console.log(firstPromise);

        // 소비자
        firstPromise.then(v => console.log(v));

2초 후, resolve 내 결과값을 호출한다.

catch

데이터를 정상적으로 가져오지 못했을 경우 (promise 내 resolve 대신 reject가 호출된 경우)
catch를 통해 reject가 반환한 데이터를 사용 할 수 있다.

let firstPromise = new Promise((resolve, reject) => {
            // 예) data 가져오는데 2초 -> 성공/실패
            setTimeout(() => {
                // resolve('data');
                reject('failed');
            }, 2000);
        });
        // console.log(firstPromise);

        // 소비자 : then, catch
        firstPromise.then(v => console.log(v))
                    .catch(error => console.log(error))

finally

성공,실패와 상관없이 수행해야 하는 기능을 위해 사용되는 키워드.
finally는 resolve, reject가 반환한 결과값은 사용하지 못한다.
즉, resolve 내 데이터는 then만, reject 내 데이터는 catch만 사용이 가능하다!

firstPromise.then(v=>console.log(v))
                    .catch(error=>console.log(error)) //reject 실행시 reject 내 데이터 출력
                    .finally((e)=>console.log(e)); //넘겨받을 수 있는 것이 없으므로, 안뜬다!

fetch

하지만 매번 Promise 객체를 만들기 귀찮다. 그래서 보통은 Promise 를 만드는 함수를 선언하고, 이를 호출한다.
Promise 를 함수로 감싸서 사용하게 되는게, 이런 함수를 fetch함수라고 한다.

function fetchResult(){
            return new Promise((resolve,reject)=>{
                if(true) resolve('성공ㅎㅎ');
                else reject('실패ㅠㅠ');
            });
        }

        fetchResult()
            .then(value =>{
                //fetch 성공 시 로직   
            })
            .catch(error=> {
                //fetch 실패 시 로직
            })
profile
풀스택 호소인

0개의 댓글