동기, 비동기, Promise

윤한영·2023년 9월 8일
0

공부슬금슬금

목록 보기
1/7

🐣 혼자 공부하는 용도로 블로그를 보면서 작성한 거라 내용이 상당상당히 비슷합니다. 혹시나 보게 될 사람이 있으시다면 참고 바랍니다!


JavaScript는 기본적으로 비동기적으로 동작한다.

동기 / 비동기

✅ 동기란?

코드를 위에서부터 순차적으로 읽어 처리하는 방식
작업 하나가 끝나면 그 다음 작업을 실행하는 방식
동시에 여러 작업을 수행할 수 없다.
흐름을 예측하기 쉽고, 실행되는 순서가 명확하다.


✅ 비동기란?

동시에 여러 작업을 수행할 수 있다.
흐름을 예측하기 어렵고 무엇이 먼저 완료될 것인지 보장할 수 없다.

📝 콜백함수는 A라는 함수의 파라미터에 B라는 함수를 사용하는 것인가? 그럼 여기서 B함수가 콜백함수가 되는건가?

  • setTimeout 은 인자로 들어온 콜백 함수를 예약하기만 하고 리턴한다.
  • setTimeout 에 의해 기다리는 동작은 본래의 코드 흐름과는 상관없이 따로따로 독립적으로 동작한다.
  • 이렇게 따로따로 동작하는 것을 비동기 작업이라고 한다.

✅ 비동기의 단점

  1. 흐름을 예측하기 어렵다
    동기 작업은 시간이 오래 걸리기는 하지만 무엇이 어떻게 진행되는지는 명확한 반면, 비동기 코드에서는 비교적 효율적이기는 하지만 무엇이 어떤 순서로 진행될지 예측하기가 상당히 어렵습니다.

  2. 콜백 지옥

    비동기 작업에서 흐름제어를 하기 위해서는 그 특성상 각각의 비동기 작업이 "끝"났을 때 뒤에 이어질 작업을 미리 부여하는 식으로 흐름을 제어합니다.

    예를 들면, setTimeout 과 같이 함수 단위로 작업을 처리하는 식으로 밖에 흐름 제어를 하지 못한다.
    만약 비동기 작업이 주르륵 이어져있다면 콜백 지옥에 빠질 수 있다!!!


Promise

  • promise 에서는 작업의 단위를 함수로 관리한다.
  • promise 는 비동기 작업의 단위이다
  • promise 를 통해 비동기 작업들을 쉽게 관리할 수 있다.

  • Promise에서 요구하는 가장 기본적인 형태
const promise01 = new Promise((resolve, reject) => {
  // 비동기 작업...
});

  • 위의 예시 설명

    1. 변수의 이름은 promise01이며, const로 선언했기 때문에 재할당이 되지 않는다. 하나의 변수로 끝까지 해당 Promise를 관리하는 것이 가독성도 좋고 유지보수하기 좋음

    2. new Promise(...)으로 Promise객체를 새롭게 만듬

    3. 생성자는 특별한 함수 하나를 인자로 받는다.
      / 이 함수의 이름은 executor, 형태는 화살표 함수


  • executor에 대한 설명

    1. executor는 첫번째 인수로 resolve, 두 번째 인수로 reject를 받는다

    2. resolveexecutor내에서 호출할 수 있는 또 다른 함수, resolve를 호출하게 된다면 "이 비동기 작업이 성공했어@!@!!!"라는 뜻이다.

    3. reject 또한 executor내에서 호출할 수 있는 또 다른 함수, reject를 호출하게 된다면 "이 비동기 작업이 실패했어~.."라는 뜻이다.


Promise의 특징으로는 new Promise(...)를 하는 순간 여기에 할당된 비동기 작업은 바로 시작된다. 앞서 이야기했듯 함수란 으레 정의하는 시점과 호출하는 시점이 다르다고 했었지만, new Promise는 그냥 바로 호출한다.


Promise가 끝나고 난 다음의 동작을 우리가 설정해줄 수 있는데, 그것이 바로 then메소드와 catch이다.

  • then메소드는 해당 Promise가 성공하고 난 후의 동작을 지정한다. / 인자로 함수를 받음
  • catch메소드는 해당 Promise가 실패하고 난 후의 동작을 지정한다. / 역시 인자로 함수를 받음
  • 위 함수들은 체인 형태로 활용할 수 있다. (연속적으로 호출할 수 있다는 말)
const promise02 = new Promise((resolve, reject) => {
  resolve();
});

promise02
	.then(() => {
  		console.log("then!");
	})
	.catch(() => {
  		console.log("catch!");
	});

// Promise에서 resolve가 호출되었기에 then!만 출력됨

재사용하기

비슷한 비동기 작업을 수행할 때마다 매번 new Promise(...)을 할 필요없이 그냥 new Promise(...) 한 것을 그대로 리턴하는 함수를 만들어 사용하면 된다.

function startAsync(age) {
  return new Promise((resolve, reject) => {
    if(age > 20) resolve();
    else reject();
  });
}

const promise03 = startAsync(25);
promise03
	.then(() => {
  		console.log("1 then!");
	})
	.catch(() => {
  		console.log("1 catch!!!");
	});

const promise04 = startAsync(15);
promise04
	.then(() => {
  		console.log("2 then!!!");
	});
	.catch(() => {
  		console.log("2 catch!!!");
	});

이제 startAsync 함수를 호출하는 순간 new Promise(...)가 실행하게 되어 비동기 작업이 시작된다.



작업 결과를 전달하기

추가 고려사항

  • executor 내부에서 에러가 throw 된다면 해당 에러로 reject가 수행된다.
  • executor의 리턴 값은 무시된다.
  • 첫 번째 reject 혹은 resolve 만 유효하다. (executor 안에서 둘 중 하나가 호출되었다면 두 번째부터는 무시된다. throw 역시 마찬가지)

Promise는 세 가지 상태를 지닌다. 대기(pending), 이행(fulfilled), 거부(rejected) 이며 fulfilled 일 때 then, rejected일 때 catch 로 등록한 동작들이 실행된다.


Promise는 비동기 작업을 생성/시작하는 부분(new Promise(...))과 작업 이후의 동작 지정 부분(then, catch)을 분리함으로써 보다 유연한 설계를 가능하도록 한다.





참고자료

0개의 댓글