notion-038

😉 헷갈렸던 & 몰랐던 부분들만 정리하는 나만의 TIL
😯 모든 강의 내용은 적지 않아요!

오늘의 소감은?
어렵다는 이유로 미루고 미뤘던 Promise에 대해 다뤄봤습니다.
왜.. 어려운지 함수 구조를 보고 알아버렸습니다!

그래도 강사님이 키우시는 고양이 소리를 중간 중간 엿들으며 열심히 학습했습니다.
웹 사이트 얼른 만들어보고 싶어요!




[1] ES6 Module

import, export로 모듈을 사용할 수 있습니다.

이를 사용하기 위해서는 웹 서버가 필요합니다.


[1-1] 장점

  • 각 JS별로 사용되는 모듈을 명시적으로 import 해오기 때문에, 사용되거나 사용되지 않는 스크립트를 추적할 수 있습니다.
  • script 태그로 로딩하는 경우, 불러오는 순서가 굉장히 중요합니다. 하지만 import로 불러오는 경우 순서는 무관합니다.
  • script src로 불러오는 것과 다르게 전역 오염이 일어나지 않습니다.




[2] 비동기 다루기 Callback

비동기란 무엇일까요?

특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 JS의 특성입니다.

sync 방식 동작의 가장 큰 단점은 요청 후 응답이 오기 전까지 브라우저가 굳어버리는 것 입니다.

따라서 비동기 방식의 통신을 따르고 있습니다.

대표적인 함수로는 addEventListener, setTimeout, setInterval, XMLHttpRequest가 있습니다.




[3] Promise

Promise는 비동기 작업을 제어하기 위해 나온 개념으로 callback hell에서 어느정도 벗어날 수 있게 해줍니다.

  • JS의 Callback Hell..

Untitled

Promise로 정의된 작업끼리는 연결이 가능하며, 이를 통해 코드의 depth가 크게 증가하지 않는 효과가 있습니다.

const promise = new Promise((resolve, reject) => {
  // promise 내부 비동기 상황 종료 시 -> resolve 함수 호출
  // promise 내부 오류 상황 -> reject 함수 호출
});


[3-1] Promise 동작원리

Promise는 비동기 작업을 조금 더 보기 편하게 해주며 제어하기 쉽게 도와줍니다.


  • .then

먼저 Promise의 then에 대해 알아보겠습니다.

function asyncPromise() {
  // ...
  return new Promise((resolve, reject) => {
    // ...
    return resolve("complete");
  });
}

// then의 result에는 resolve를 호출하며 넘긴 complete가 들어있습니다.
asyncPromise().then((result) => console.log(result));
  1. promise 내부 비동기 상황이 종료됩니다.
  2. resolve함수 호출되어 complete를 then에 넘깁니다.
  3. then함수는 비동기 상황이 성공적으로 종료되어 값을 넘겨받았을 때 실행되는 함수이므로 console.log(result)가 실행됩니다.

(오류가 생기면 reject 함수가 호출됩니다.)


만약 Promise의 then내에서 promise를 return할 경우 계속해서 이어지는 형태가 됩니다.

이를 Promise chain이라 합니다.

asyncPromise()
  .then((result) => {
    return asyncPromise(result);
  })
  .then((result) => {
    return asyncPromise(result);
  })
  .then((result) => {
    return asyncPromise(result);
  });

😮 아까의 callback Hell과는 다르게 callback depth를 1단계로 줄여진 것을 확인할 수 있습니다.


  • .catch

또한 Promise Chain 중 작업이 실패했을 경우, .catch로 잡을 수 있습니다.

보통은 catch를 넣지 않으면 디버깅이 힘들기 때문에, promise chain 내 catch를 많이 사용합니다.

asyncPromise()
  .then((result) => {
    return asyncPromise(result);
  })
  .then((result) => {
    return asyncPromise(result);
  })
  .then((result) => {
    return asyncPromise(result);
  })
  .catch((e) => {
    alert("에러 발생!");
  });


  • .finally

then은 성공적으로 종료되었을 때 호출되는 함수라면,

finally는 작업의 메모리 정리와 같은 성공/실패 여부와 상관 없이 호출해야 하는 코드를 담습니다.

asyncPromise()
  .then((result) => {
    return asyncPromise(result);
  })
  .then((result) => {
    return asyncPromise(result);
  })
  .catch((e) => {
    alert("에러 발생!");
  })
  .finally(() => {
    alert("성공 실패와 상관 없이 작업은 끝났어요!");
  });


[3-2] Promise 내장함수

  • Promise.resolve

주어진 값으로 이행하는 Promise.then 객체를 만듭니다.

주어진 값이 Promise인 경우 해당 Promise가 반환됩니다.


  • Promise.reject

주어진 값으로 reject 처리된 Promise.then 객체를 만듭니다.

주어진 값이 Promise인 경우 해당 Promise가 반환됩니다.


  • Promise.all(iterable)

여러 promise를 동시에 처리할 때 유용합니다.

클라이언트 측에서 여러개의 api를 호출해서 사용할 때 많이 사용합니다.

const promise1 = delay(2000);
const promise2 = delay(4000);
const promise3 = delay(8000);

Promise.all([promise1, promise2, promise3]).then(() => {
  // promise1, promise2, promise3이 모두 처리된 이후에 호출 (8초 뒤)
});


  • Promise.race(iterable)

여러개의 promise 중 하나라도 resolve, 또는 reject 되면 종료됩니다.

function getRanNum(min, max) {
  // 랜덤 숫자 구하기
}

const promiseRaceCats = [1, 2, 3, 4, 5].map((cat) => {
  const raceTime = getRanNum(1000, 5000);
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(`${cat}번 고양이 완주!`);
      resolve(`${cat}반 고양이 승리!`);
    }, raceTime);
  });
});

Promise.race(promiseRaceCats).then((message) => consolle.log(message));
// 가장 빠르게 완주한 고양이의 번호가 속한 resolve 내 문자열만 race().then()함수로 전달된다.


  • Promise.any(iterable)

여러 promise 중 하나라도 resolve 되면 종료됩니다.

race는 resolve/reject에 상관 없이 종료된다면, any는 하나가 무조건 resolve 되어야 종료됩니다.

만약 첫번째 promise가 reject 된다면 무시합니다.

function getRanNum(min, max) {
  // 랜덤 숫자 구하기
}

const promiseRaceCats = [1, 2, 3, 4, 5].map((cat) => {
  const raceTime = getRanNum(1000, 5000);
  return new Promise(resolve, (reject) => {
    if (n === 1) return reject(`${cat}번 고양이 기권!`);

    setTimeout(() => {
      console.log(`${cat}번 고양이 완주!`);
      resolve(`${cat}반 고양이 승리!`);
    }, raceTime);
  });
});

Promise.any(promiseRaceCats).then((message) => consolle.log(message));
// 1번 고양이는 reject됨으로 무시된다.


  • Promise.allSettled(iterable)

여러 promise들이 성공했거나 실패했거나 상관 없이 모두 이행되었을 경우를 처리합니다.

function getRanNum(min, max) {
  // 랜덤 숫자 구하기
}

const promiseRaceCats = [1, 2, 3, 4, 5].map((cat) => {
  const raceTime = getRanNum(1000, 5000);
  return new Promise(resolve, (reject) => {
    if (n === 1) return reject(`${cat}번 고양이 기권!`);

    setTimeout(() => {
      console.log(`${cat}번 고양이 완주!`);
      resolve(`${cat}반 고양이 승리!`);
    }, raceTime);
  });
});

Promise.allSettled(promiseRaceCats).then((results) => consolle.log(results));
// 어떤 고양이가 실패하고 성공하든 마지막에 results를 출력한다.




[4] async, await

Promise가 callback depth를 1단계로 줄여주긴 하지만, 여전히 불편한 점이 있습니다.

특히 코드의 흐름과 실제 실행 순서가 일치하지 않는다는 점에서 코드의 가독성을 어렵게 만듭니다.

const work = () => {
  console.log("start");

  delay(1000)
    .then(() => {
      console.log("1");
      return delay(1000);
    })
    .then(() => {
      console.log("2");
      return delay(1000);
    })
    .then(() => {
      console.log("done!");
      return delay(1000);
    });

  console.log("working...");
};

work();
// 코드의 흐름만 보면 **start -> 1 -> 2 -> done! -> working...** 순으로 진행되어야 하지만
// 실제로는 **start -> working... -> 1 -> 2 -> done!** 순서인 것을 알 수 있습니다.
// 코드의 흐름 !== 실행 순서

console.log(’working...’)은 코드의 흐름을 보여주기 위해 임시로 넣었습니다.


async와 await를 사용하면 Promise를 동기 코드처럼 보이게 짤 수 있습니다.

실행은 여전히 비동기로 동작합니다.

const work = async () => {
  console.log("start");

  await delay(1000);
  console.log("1");

  await delay(1000);
  console.log("2");

  await delay(1000);
  console.log("done!");
};

work();
// 코드의 흐름과 실행 순서가 유사합니다.

코드의 흐름과 실행 순서가 동일하며, depth 또한 Promise보다 더 얕아지는 것을 볼 수 있습니다.


[4-1] async 함수

함수 선언은 크게 두 가지 방식이 있습니다.

// ---------- 1
async function asyncFunc () {
  // ...
}

// ----------- 2
const asyncFunc = async () {
  // ...
}

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

따라서 async 키워드가 결과적으로는 Promise 내에서 돌아간다고 생각할 수 있습니다.


출처

프로그래머스

댓글남기기