23/10/21

Laejun Kim·2023년 10월 21일
0

TIL

목록 보기
19/89
post-thumbnail

JavaScript

Promise

Promise가 왜 필요함?

  • 가장 쉬운 대답은 '콜백 지옥을 방지하기 위해'
    => promise와 then을 사용하면 코드를 읽는 것이 수월해진다.

Promise 생성과 사용

  • new Promise 키워드를 이용해서 생성.

  • promise는 인자로 함수를 받고 그 함수는 resolve function(보통 resolve라 명명)과 reject function(보통 reject라 명명) 두가지를 parameter를 가진다.

  • resolve나 reject 함수는 내가 직접 정의하는 것이 아니고 그냥 promise 내에서 promies가 달성(?), 혹은 실패 했다는 것을 나타내준다(제대로 이해한게 맞나 모르겠네...)

  • resolve() 의 ( ) 안에는 then에서 인자로 받을 것을 지정할 수 있고 이건 string, number, array, obj 뭐든 상관없다.

  • reject() 의 ( ) 안에는 catch 에서 사용할 인자를 지정한다.

const setTimer = (duration) => {
  const promise = new Promise((resolve, reject) => {
    //여기다 이제 무슨 일이 일어나야 할지 적음
    setTimeout(() => {
      resolve("Done!"); //resolve의 ( )안에 then에 넘겨줄 것을 입력.string이든 array이든 객체든 상관없음
    }, duration);
  });
  return promise; // 이렇게 꼭 promise를 return 해줘야함
};

function trackUserHandler() {
  navigator.geolocation.getCurrentPosition(
    (posData) => {
      setTimer(2000).then((data) => {
        console.log(data, posData);
      }); // promise obj 에 then 메서드를 쓸 수 있다.
    },
    (error) => {
      console.log(error);
    }
  );
  setTimer(1000).then(() => {
    console.log("timer done!");
  });
  console.log("getting position...");
}

button.addEventListener("click", trackUserHandler);

에러 핸들링

  • then은 인자를 둘 받을수 있음. 첫번째는 promise가 resolve 되었을때 수행할 작업이고, 두번째 인자는 promise가 reject 되었을때 수행할 작업. 하지만 then의 두번째 인자로 에러 핸들링을 하는 것보다 catch를 쓰는 것이 더 읽기 편함

  • catch 블럭은 promise chain의 어디에든 추가할 수 있지만 어디에 추가하는 지에 따라 동작이 달라짐

.then() //여기서 에러가 나면
.then() //얘 건너 뛰고
.then() //얘도 건너뛰고
.catch() //얘가 그 에러를 잡아내서 이녀석의 코드가 실행된다.
.then() // 그리고 catch 이후의 then부터 다시 실행됨.

에러가 났을때 promise chain 전체를 멈추고 싶다면 catch를 마지막에 달아주면 되겠지.

catch블럭을 하나 이상 설정하는 것도 물론 가능하다.

async & await

async, await이 뭐야?

  • async, await 은 then 과 catch를 생략할수 있게 해주는 구문이다

  • async await은 오직 함수에서만 쓸수 있고, 함수 선언문에선 function 키워드 앞에 async 키워드를 추가해서 이를 활성화 함.

  • function 키워드 없이 활성화된 함수(표현식)는 등호 옆에 async 를 붙여줌. 아래가 예시.

const setTimer = async () => { }
  • 아무튼 이렇게 async를 붙이면 그 함수는 자동으로 프로미스를 반환함(return을 따로 적어주지 않은 경우에도 마찬가지). 그리고 그 함수를 호출할 때 then도 붙일수 있게됨.

[follow up 10/29]
위에서 async 를 붙였을때 프로미스를 반환한다고 한것은 선택사항이 아니다! 즉 return 문을 따로 명시적으로 적어둔다고 해서 그 return문의 내용을 반환하게 만들 수는 없다. return문으로 뭘 반환하겠다고 적어놔도 무조건 반환되는 것은 promise다

async sayhi(){
  return "hi! nice to meet ya"
}

같은게 있어서 마치 "hi! nice to meet ya" 스트링이 반환될것 같아 보여도 반환되는 것은 무조건 promise라는 의미이다.


  • await를 프로미스 앞에 추가하는데 await는 그 프로미스가 resolve/reject 될때까지 기다렸다가 그 다음 줄을 실행함.
async function trackUserHandler() {
  const posData = await getPosition();
  const timerData = await setTimer(2000);
  console.log(timerData, posData);}

위 예시에서는 function 앞에 async를 붙이고, promise인 getPosition과 setTimer 앞에 await을 붙였다.
그리고 getPosition의 결과를 posData에 저장하고, setTimer의 결과를 timerData에 저장한 다음 그걸 콘솔에 찍은 것.
setTimer와 관련된 코드는 윗줄의 getPosition이 resolve될때까지 실행되지 않는다.

에러 핸들링

  • async & await를 쓸때 오류는 try catch 로 한다.
async function trackUserHandler() {
  let posData;
  let timerData;
  try {
    posData = await getPosition();
    timerData = await setTimer(2000);
  } catch (error) {
    console.log(error);
  }
  console.log(timerData, posData);

두 promise가 둘다 resolve되면 catch 블록은 건너뛰고 그 다음줄이 실행되고, 위의 promies가 실행되면 그 아래 promise는 건너뛰고 catch 블록이 실행됨.
마지막 줄의 console.log는 error가 나건 resolve가 되건 상관없이 무조건 실행된다.

단점

  • async await 으로 then catch를 대체했을 때 발생하는 단점은
    then catch에서 then catch블록 아래의 코드가 즉시 실행되었던 것과 달리 async await 에서는 await붙은 애들이 전부 다 완료될때까지 함수 내의 다른 코드들이 실행되지 못하고 프로미스를 기다리게 된다는 것.
    그래서 동시에 여러 작업을 실행하거나 시작해야하는 함수일 경우에는 async await이 적절하지 않을 수도 있다.

promise 관련 메서드

Promise.race()

  • 배열로 전달된 프로미스끼리 경주를 시킨다!

  • 인자로 복수의 프로미스를 받아서 그중 가장 먼저 실행된 녀석의 반환된 결과를 다음 프로미스 체인으로 넘긴다

Promise.race([promise1(),promise2()]).then(data=>{console.log(data);})

이런식으로 적으면 promise1과 promise2중 더 빨리 완료된 것의 반환값이 then에 전달됨. 경주에서 이기지 못한 프로미스는 취소되는 것이 아니라 실행은 되지만 그 결과가 무시된다.

Promise.all()

  • 배열로 전달된 프로미스 전부의 결과를 전달한다. 모든 프로미스가 완료되고 그것들의 결과를 사용하고 싶을 때 사용하기 좋음.
Promise.all([promise1(),promise2()]).then(promiseData=>{console.log(promiseData);})

이러면 promise1과 promise2 의 결과를 배열로 콘솔에 찍음.

Promise.all은 프로미스중 하나라도 실패하면 전체가 취소됨.

Promise.allSettled()

  • allSettled 메서드는 all메서드랑 비슷하지만 뭐 하나가 실패해도 다른 프로미스의 실행을 취소하지 않고 대신 모든 프로미스의 결과가 나올 때까지 기다렸다가 각 프로미스의 성공/실패 여부와 관련된 정보들을 배열에 담아서 제공함.

personal remark

계속 찝찝한채로 애써 무시하고 있었던 부분중 하나인 promise를 공부해서 그래도 조금은 뭔지 감이 잡히게 되었다!

아직은 어렵게 느껴지는 부분들이 있지만 그래도 뭐가 어떻게 동작하는지는 대충 알것 같다.

완전히 이해하게 되려면 직접 코드를 쓸때 사용해봐야만 할것 같은데, 다음에 뭐 만들어 볼때 꼭 의도적으로 promise와 관련된 코드를 써보도록 하자!

0개의 댓글