티스토리 뷰

Web/JAVASCRIPT

Promise 기초 정리

HAN_PY 2022. 3. 4. 20:44
반응형

Promise에 대한 기초에 대해 정리해 보려고 한다. 우선은 아래의 코드를 보자. 이해가 되는가? 이해가 되지 않는다면, 이곳에서 알아가 보자.

 

 const Loading = async () => {
    await new Promise((resolve) => setTimeout(resolve, 1000))
  };

 

 

1. promise

promise는 시간이 걸리는 비동기 작업에 대해, 성공/실패를 나타내는 객체이다. 그리고 함수에 콜백을 전달하는것이 아닌 콜백을 첨부하는 객체라고도 할 수 있다. 이말을 조금 더 쉽게 적어보면, 성공/실패를 나타내는 함수를 호출할 수 있게 담아서 전달해 주는 것이라고 할 수 있다. 아래의 예시를 보자.

 

function successCallback(result) {
  console.log("Audio file ready at URL: " + result);
}

function failureCallback(error) {
  console.log("Error generating audio file: " + error);
}

createAudioFileAsync(audioSettings, successCallback, failureCallback);
  • createAudioFileAsync 함수의 audioSettings에 따라서 성공 시 successCallback 함수를 주고 실패시 failureCallback 함수를 주는 느낌이라고 할 수 있다.

위를 Promise를 반환하도록 수정해 보면 아래와 같다.

createAudioFileAsync(audioSettings).then(successCallback, failureCallback);

그리고 조금 더 간단히 적으면 아래와 같다.

const promise = createAudioFileAsync(audioSettings);
promise.then(successCallback, failureCallback);
  • 특징1. promise는 createAudioFileAsync를 완료하기 전에는 then을 실행하지 않는다.
  • 특징2. then을 여러번 사용하여 여러개의 콜백을 추가 할 수 있고, 각각의 콜백은 순서대로 실행된다.

 

2. 연속된 비동기 처리하기

우선 기본적은 연속된 콜백함수를 보면 아래와 같다. 혹자는 콜백지옥이라고도 한다.

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

이를 promise로 만들면 아래와 같다.

doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
  • catchthan(null, failureCallback)의 축약이다.

위를 화살표 함수로 아래와 같이 좀 더 간단히 표현할 수 있다.

doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
  console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
  • catch than(null, failureCallback)의 축약이다.
  • return 으로 반환하는 값이 반드시 있어야 한다. 반환값이 없다면 이전 promise의 결과를 받지 못한다.
  • 위의 .then(res => do(res)).then(res => {return do(res)}) 와 같다.
  • 기본적으로 연속되는 promise에서는 예외/오류가 발생하면 코드 작동을 멈추고 아래에서 catch를 바로 찾는다. 이를 코드로 작성해보면, 아래와 같은 느낌이라고 이해하면 좋다. 바로 아래 코드는 참고만 하자.
try {
  const result = syncDoSomething();
  const newResult = syncDoSomethingElse(result);
  const finalResult = syncDoThirdThing(newResult);
  console.log(`Got the final result: ${finalResult}`);
} catch(error) {
  failureCallback(error);
}

 

추가로 비동기 코드를 async/await 구문을 사용하면 좀 더 매력적인 코드가 아래처럼 된다.

 

async function foo() {
  try {
    const result = await doSomething();
    const newResult = await doSomethingElse(result);
    const finalResult = await doThirdThing(newResult);
    console.log(`Got the final result: ${finalResult}`);
  } catch(error) {
    failureCallback(error);
  }
}

 

 

3. new Promise

보통 비동기 함수는 promise를 반환하지만, 그렇지 않은 함수도 있다. 가장 대표적인 것이 바로 setTimeout 함수이다. 아래의 예를 보면 saySomething()이 실패/오류 가 있더라도 아무것도 잡아내지 않는다. 

 

setTimeout(() => saySomething("10 seconds passed"), 10000);

 

이러한  setTimeout에 관한 문제를 해결하는 방법으로는 아래와 같이 Promise로 감싸는 방법이 있다.

 

const wait = ms => new Promise(resolve => setTimeout(resolve, ms));

wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback);
  • 위의 코드는 resolve를 사용했다. 기본적으로 setTimeout 함수는 fail/error가 일어나지 않으므로 reject는 사용하지 않고 resolve만 사용했다.

 

resolve에 대해 조금더 알아보자.

 

resolve

 Promise.resolve(value) 메서드는 Promise.then 객체를 반환한다. 쉽게 말하면, 코드가 resolve를 만나면, then으로 이동해서 코드가 진행된다고 할 수 있다. 아래의 예를 통해 이해를 진행해 보자

const promise1 = Promise.resolve(123);

promise1.then((value) => {
  console.log(value);
  // expected output: 123
});
  • Promise.resolve()에 들어간 인자가 then의 value로 들어가는 것을 확인할 수있다.

 

var original = Promise.resolve(33);
var cast = Promise.resolve(original);
cast.then(function(value) {
  console.log('value: ' + value);
});
console.log('original === cast ? ' + (original === cast));

// 로그 순서:
// original === cast ? true
// value: 33
  • console.log를 자세히 보면, 로그의 순서가 반대인 것을 확인 할 수 있다. 이는 then이 비동기로 호출되기 때문이다.

 

then/catch

then은 resolve를 만나면, 실행된다는 것을 알았다. catch는 reject를 만나면 실행된다.

var p1 = new Promise(function(resolve, reject) {
  resolve("성공!");
  // 또는
  // reject("오류!");
});

p1.then(function(value) {
  console.log(value); // 성공!
}, function(reason) {
  console.log(reason); // 오류!
});
  • 위의 코드는 catch가 없다!! 그럼 reject를 만나면 어떻게 될까?
  • 기본적으로 then(null, function())catch(function())은 같다. 즉, catch를 then의 두번째 인자로 포함시켜 사용해도 된다는 말이다.
  • 아래의 예제를 보면 이해가 더 쉽게 된다.
Promise.reject()
  .then(() => 99, () => 42) // onRejected에서는 42를 곧 이행할 프로미스로 감싸서 반환함
  .then(solution => console.log(solution + '로 이행함')); // 42로 이행함

 

 

4. 비동기작업 병렬 처리

Promise.all()

기본적으로 비동기 작업을 병렬로 처리하기 위해서 Promise.all()을 사용한다. 예제를 통해 확인해 보자.

var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("foo");
  }, 100);
});

Promise.all([p1, p2, p3]).then(values => {
  console.log(values); // [3, 1337, "foo"]
});
  • 기본적으로 p1, p2, p3가 모두 완료 될때까지 기다린 후에 then을 시행한다.
  • 기본적으로 frontend에서는 화면이 보여지기 전에 데이터를 다 받아오는 것을 기다리면서 사용할 수 있다.
  • 만약 병렬로 처리할 작업중에 실패한 작업이 하나라도 있으면 어떻게 될까? 아래의 예를 보자.

 

var p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('하나'), 1000);
});
var p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('둘'), 2000);
});
var p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('셋'), 3000);
});
var p4 = new Promise((resolve, reject) => {
  setTimeout(() => resolve('넷'), 4000);
});
var p5 = new Promise((resolve, reject) => {
  reject(new Error('거부'));
});


// .catch 사용:
Promise.all([p1, p2, p3, p4, p5])
.then(values => {
  console.log(values);
})
.catch(error => {
  console.log(error.message)
});

// 콘솔 출력값:
// "거부"
  • Promise.all()에서는 하나라도 실패한 작업이 있다면, 실패한 작업만 출력되고 나머지는 사라진다.

 

let result;
for (const f of [func1, func2, func3]) {
  result = await f(result);
}
/* use last result (i.e. result3) */
  • 위과 같이 async/await를 사용하여 순차적 구성을 사용할 수 있다.

 

 

5. 정리

그렇다면 처음 부분의 코드를 다시 확인해 보자.

 const Loading = async () => {
    await new Promise((resolve) => setTimeout(resolve, 1000))
  };
  • setTimeout 함수는 기본적으로 비동기 이다. 이러한 함수는 리턴값이 없기 때문에 Promise로 만들어 리턴값을 받을 수 있게 하여, Loading 이 진행이 완료되는 시점을 파악할 수 있게 만든 로직임을 짐작할 수 있다.
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함