Promise

RHUK2Β·2021λ…„ 6μ›” 11일
0

Javascript

λͺ©λ‘ 보기
51/56
post-custom-banner

πŸ“š Reference


λ“œλ¦Όμ½”λ”© by μ—˜λ¦¬, https://www.youtube.com/watch?v=s1vpVCrT8f4
javascript.info, https://ko.javascript.info/promise-basics
MDN, https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise

μ°Έκ³  μ‚¬μ΄νŠΈμ— λ‚΄μš©μ„ 개인적으둜 λ³΅μŠ΅ν•˜κΈ° νŽΈν•˜λ„λ‘ μž¬κ΅¬μ„±ν•œ κΈ€μž…λ‹ˆλ‹€.
μžμ„Έν•œ μ„€λͺ…은 μ°Έκ³  μ‚¬μ΄νŠΈλ₯Ό μ‚΄νŽ΄λ³΄μ‹œκΈ° λ°”λžλ‹ˆλ‹€.


Promiseκ°€ λ“±μž₯ν•œ 이유


기쑴에 비동기 μ²˜λ¦¬λŠ” 비동기 ν•¨μˆ˜μ˜ 콜백 ν•¨μˆ˜λ₯Ό μ „λ‹¬ν•˜λŠ” λ°©μ‹μœΌλ‘œ ν•΄κ²°ν–ˆμŠ΅λ‹ˆλ‹€.

ν•˜μ§€λ§Œ 비동기 λ™μ•ˆμ— ν•΄μ•Όν•  일이 λ§Žμ•„μ§€κ³  κ·Έ μˆœμ„œλ₯Ό 보μž₯ν•˜κΈ° μœ„ν•΄μ„œ 콜백 ν•¨μˆ˜λ₯Ό 인자둜 λ°›λŠ” ν•¨μˆ˜λ“€μ„ μ€‘μ²©ν•΄μ„œ μ‚¬μš©ν•˜λ‹€λ³΄λ‹ˆ 콜백 지μ˜₯이 λ°œμƒν•˜κ²Œ 되고 μ΄λŠ” 가독성이 떨어지고 μœ μ§€λ³΄μˆ˜λ₯Ό μ–΄λ ΅κ²Œ λ§Œλ“€μ—ˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό λ³΄μ™„ν•˜κ³ μž λ“±μž₯ν•œ 것이 Promiseμž…λ‹ˆλ‹€.

μ•„λž˜λŠ” 콜백 지μ˜₯에 λŒ€ν•œ μ˜ˆμ‹œμž…λ‹ˆλ‹€.

class UserStorage {
  loginUser(id, password, onSuccess, onError) {
    setTimeout(() => {
      if (
        (id === "Tomas" && password === "1234") ||
        (id === "James" && password === "5678")
      ) {
        onSuccess(id);
      } else {
        onError(new Error("Not Found"));
      }
    }, 2000);
  }

  getRoles(user, onSuccess, onError) {
    setTimeout(() => {
      if (user === "Tomas") {
        onSuccess({ name: "Tomas", role: "admin" });
      } else {
        onError(new Error("No Access"));
      }
    }, 1000);
  }
}

const userStorage = new UserStorage();
const id = prompt("Enter your id");
const password = prompt("Enter your password");

userStorage.loginUser(
  id,
  password,
  (user) => {
    userStorage.getRoles(
      user,
      (userWithRole) =>
        alert(`Hello! ${userWithRole.name}. You have a ${userWithRole.role}`),
      (error) => console.log(error)
    );
  },
  (error) => console.log(error)
);

μ‚¬μš©μžλ‘œλΆ€ν„° id와 passwordλ₯Ό μž…λ ₯ λ°›μ•„μ„œ μœ μ €μΈμ§€ ν™•μΈν•˜κ³  μœ μ € idλ₯Ό 톡해 κΆŒν•œμ„ λΆ€μ—¬ν•˜λŠ” μ½”λ“œμž…λ‹ˆλ‹€. 콜백 ν•¨μˆ˜λ₯Ό 인자둜 λ°›λŠ” 비동기 ν•¨μˆ˜μ˜ 쀑첩이 두 λ²ˆλ°–μ— 생기지 μ•Šμ•˜μ§€λ§Œ 맀우 가독성이 떨어지고 μœ μ§€λ³΄μˆ˜κ°€ μ–΄λ €μ›Œμ§‘λ‹ˆλ‹€.


제곡자 Promise


Promise

PromiseλŠ” μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ 객체둜 비동기 처리λ₯Ό μœ„ν•΄ μ‚¬μš©λ©λ‹ˆλ‹€. μ•„λž˜μ™€ 같이 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.

const promise = new Promise(function (resolve, reject) {
  // executor
  console.log('Doing something...(Network, Read files)');
}); 

// 객체 생성 μˆœκ°„ λ°”λ‘œ 싀행됨
>> Doing something...(Network, Read files)

new Promise()에 μ „λ‹¬λ˜λŠ” ν•¨μˆ˜λŠ” executor()(μ‹€ν–‰μž, μ‹€ν–‰ ν•¨μˆ˜)라고 λΆ€λ¦…λ‹ˆλ‹€. executor()λŠ” new Promise()κ°€ λ§Œλ“€μ–΄μ§ˆ λ•Œ μžλ™μœΌλ‘œ μ‹€ν–‰λ©λ‹ˆλ‹€.

executor()의 인자 resolve()와 reject()λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ 자체 μ œκ³΅ν•˜λŠ” μ½œλ°±μž…λ‹ˆλ‹€. κ°œλ°œμžλŠ” resolve()와 reject()λ₯Ό μ‹ κ²½ 쓰지 μ•Šκ³  executor() μ•ˆ μ½”λ“œλ§Œ μž‘μ„±ν•˜λ©΄ λ©λ‹ˆλ‹€.

λŒ€μ‹  executor()에선 κ²°κ³Όλ₯Ό μ¦‰μ‹œ μ–»λ“  늦게 μ–»λ“  상관없이 상황에 따라 인수둜 λ„˜κ²¨μ€€ 콜백 쀑 ν•˜λ‚˜λ₯Ό λ°˜λ“œμ‹œ ν˜ΈμΆœν•΄μ•Ό ν•©λ‹ˆλ‹€.

resolve(value)
일이 μ„±κ³΅μ μœΌλ‘œ λλ‚œ 경우 κ·Έ κ²°κ³Όλ₯Ό λ‚˜νƒ€λ‚΄λŠ” value와 ν•¨κ»˜ 호좜

reject(error)
μ—λŸ¬ λ°œμƒ μ‹œ μ—λŸ¬ 객체λ₯Ό λ‚˜νƒ€λ‚΄λŠ” error와 ν•¨κ»˜ 호좜

μš”μ•½ν•˜λ©΄ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€. executor()λŠ” μžλ™μœΌλ‘œ μ‹€ν–‰λ˜λŠ”λ° μ—¬κΈ°μ„œ μ›ν•˜λŠ” 일이 μ²˜λ¦¬λ©λ‹ˆλ‹€. μ²˜λ¦¬κ°€ λλ‚˜λ©΄ executor()λŠ” 처리 성곡 여뢀에 따라 resolve()λ‚˜ reject()λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.

ν•œνŽΈ, new Promise() μƒμ„±μžκ°€ λ°˜ν™˜ν•˜λŠ” promise κ°μ²΄λŠ” λ‹€μŒκ³Ό 같은 λ‚΄λΆ€ ν”„λ‘œνΌν‹°λ₯Ό κ°–μŠ΅λ‹ˆλ‹€.

state
μ²˜μŒμ—” pendingμ΄μ—ˆλ‹€κ°€ resolve()κ°€ 호좜되면 fulfilled, rejectκ°€ 호좜되면 rejected둜 λ³€ν•©λ‹ˆλ‹€.

result
μ²˜μŒμ—” undefinedμ΄μ—ˆλ‹€κ°€ resolve(value)κ°€ 호좜되면 value둜, reject(error)κ°€ 호좜되면 error둜 λ³€ν•©λ‹ˆλ‹€.

λ”°λΌμ„œ executor()λŠ” μ•„λž˜ κ·Έλ¦Όκ³Ό 같이 promise의 μƒνƒœλ₯Ό λ‘˜ 쀑 ν•˜λ‚˜λ‘œ λ³€ν™”μ‹œν‚΅λ‹ˆλ‹€.

μž‘μ—…μ΄ μ„±κ³΅μ μœΌλ‘œ 끝났을 λ•Œ

const promise = new Promise(function(resolve, reject) {
  // ν”„λΌλ―ΈμŠ€κ°€ λ§Œλ“€μ–΄μ§€λ©΄ executor ν•¨μˆ˜λŠ” μžλ™μœΌλ‘œ μ‹€ν–‰λ©λ‹ˆλ‹€.

  // 1초 뒀에 일이 μ„±κ³΅μ μœΌλ‘œ λλ‚¬λ‹€λŠ” μ‹ ν˜Έκ°€ μ „λ‹¬λ˜λ©΄μ„œ resultλŠ” 'done'이 λ©λ‹ˆλ‹€.
  setTimeout(() => resolve("done"), 1000);
});

μž‘μ—… 쀑 μ—λŸ¬κ°€ λ°œμƒν–ˆμ„ λ•Œ

const promise = new Promise(function(resolve, reject) {
  // 1초 뒀에 μ—λŸ¬μ™€ ν•¨κ»˜ 싀행이 μ’…λ£Œλ˜μ—ˆλ‹€λŠ” μ‹ ν˜Έλ₯Ό λ³΄λƒ…λ‹ˆλ‹€.
  setTimeout(() => reject(new Error("μ—λŸ¬ λ°œμƒ!")), 1000);
});

πŸ’₯ ν”„λΌλ―ΈμŠ€λŠ” 성곡 λ˜λŠ” μ‹€νŒ¨λ§Œ ν•©λ‹ˆλ‹€.
executor()λŠ” resolve()λ‚˜ reject() 쀑 ν•˜λ‚˜λ₯Ό λ°˜λ“œμ‹œ ν˜ΈμΆœν•΄μ•Ό ν•©λ‹ˆλ‹€. μ΄λ•Œ λ³€κ²½λœ μƒνƒœλŠ” 더 이상 λ³€ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μ²˜λ¦¬κ°€ λλ‚œ ν”„λΌλ―ΈμŠ€μ— resolve와 rejectλ₯Ό ν˜ΈμΆœν•˜λ©΄ λ¬΄μ‹œλ©λ‹ˆλ‹€.

const promise = new Promise(function(resolve, reject) {
  resolve("done");

  reject(new Error("…")); // λ¬΄μ‹œλ¨
  setTimeout(() => resolve("…")); // λ¬΄μ‹œλ¨
});

μ΄λ ‡κ²Œ executor()에 μ˜ν•΄ μ²˜λ¦¬κ°€ λλ‚œ 일은 κ²°κ³Ό ν˜Ήμ€ μ—λŸ¬λ§Œ κ°€μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.

여기에 λ”ν•˜μ—¬ resolve()λ‚˜ reject()λŠ” 인수λ₯Ό ν•˜λ‚˜λ§Œ λ°›κ³ (ν˜Ήμ€ 아무것도 받지 μ•ŠμŒ) κ·Έ μ΄μ™Έμ˜ μΈμˆ˜λŠ” λ¬΄μ‹œν•œλ‹€λŠ” νŠΉμ„±λ„ μžˆμŠ΅λ‹ˆλ‹€.


μ†ŒλΉ„μž then, catch, finally


then

then()은 ν”„λΌλ―ΈμŠ€μ—μ„œ κ°€μž₯ μ€‘μš”ν•˜κ³  기본이 λ˜λŠ” λ©”μ„œλ“œμž…λ‹ˆλ‹€.

문법은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

promise.then(
  function(result) { /* κ²°κ³Ό(result)λ₯Ό λ‹€λ£Ήλ‹ˆλ‹€ */ },
  function(error) { /* μ—λŸ¬(error)λ₯Ό λ‹€λ£Ήλ‹ˆλ‹€ */ }
);

then()의 첫 번째 μΈμˆ˜λŠ” ν”„λΌλ―ΈμŠ€κ°€ μ΄ν–‰λ˜μ—ˆμ„ λ•Œ μ‹€ν–‰λ˜λŠ” ν•¨μˆ˜μ΄κ³ , μ—¬κΈ°μ„œ μ‹€ν–‰ κ²°κ³Όλ₯Ό λ°›μŠ΅λ‹ˆλ‹€.
then()의 두 번째 μΈμˆ˜λŠ” ν”„λΌλ―ΈμŠ€κ°€ κ±°λΆ€λ˜μ—ˆμ„ λ•Œ μ‹€ν–‰λ˜λŠ” ν•¨μˆ˜μ΄κ³ , μ—¬κΈ°μ„œ μ—λŸ¬λ₯Ό λ°›μŠ΅λ‹ˆλ‹€.

μ•„λž˜ μ˜ˆμ‹œλŠ” μ„±κ³΅μ μœΌλ‘œ μ΄ν–‰λœ ν”„λΌλ―ΈμŠ€μ— μ–΄λ–»κ²Œ λ°˜μ‘ν•˜λŠ”μ§€ λ³΄μ—¬μ€λ‹ˆλ‹€.

const promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve("done!"), 1000);
});

// resolve ν•¨μˆ˜λŠ” .then의 첫 번째 ν•¨μˆ˜(인수)λ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€.
promise.then(
  result => alert(result), // 1초 ν›„ "done!"을 좜λ ₯
  error => alert(error) // μ‹€ν–‰λ˜μ§€ μ•ŠμŒ
);

첫 번째 ν•¨μˆ˜κ°€ μ‹€ν–‰λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

ν”„λΌλ―ΈμŠ€κ°€ κ±°λΆ€λœ κ²½μš°μ—λŠ” μ•„λž˜μ™€ 같이 두 번째 ν•¨μˆ˜κ°€ μ‹€ν–‰λ©λ‹ˆλ‹€.

const promise = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("μ—λŸ¬ λ°œμƒ!")), 1000);
});

// reject ν•¨μˆ˜λŠ” .then의 두 번째 ν•¨μˆ˜λ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€.
promise.then(
  result => alert(result), // μ‹€ν–‰λ˜μ§€ μ•ŠμŒ
  error => alert(error) // 1초 ν›„ "Error: μ—λŸ¬ λ°œμƒ!"λ₯Ό 좜λ ₯
);

μž‘μ—…μ΄ μ„±κ³΅μ μœΌλ‘œ 처리된 경우만 닀루고 μ‹Άλ‹€λ©΄ then()에 인수λ₯Ό ν•˜λ‚˜λ§Œ μ „λ‹¬ν•˜λ©΄ λ©λ‹ˆλ‹€.

const promise = new Promise(resolve => {
  setTimeout(() => resolve("done!"), 1000);
});

promise.then(result => alert(result)); // 1초 λ’€ "done!" 좜λ ₯
// promise.then(alert); μΆ•μ•½ ν‘œν˜„

catch

μ—λŸ¬κ°€ λ°œμƒν•œ 경우만 닀루고 μ‹Άλ‹€λ©΄ then(null, errorHandlingFunction)κ³Ό 같이 null을 첫 번째 인수둜 μ „λ‹¬ν•˜λ©΄ λ©λ‹ˆλ‹€. catch(errorHandlingFunction)λ₯Ό 써도 λ˜λŠ”λ°, catch()λŠ” then()에 null을 μ „λ‹¬ν•˜λŠ” 것과 λ™μΌν•˜κ²Œ μž‘λ™ν•©λ‹ˆλ‹€.

const promise = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error("μ—λŸ¬ λ°œμƒ!")), 1000);
});

// .catch(f)λŠ” promise.then(null, f)κ³Ό λ™μΌν•˜κ²Œ μž‘λ™ν•©λ‹ˆλ‹€
promise.catch(error => alert(error));
// promise.catch(alert); μΆ•μ•½ ν‘œν˜„

catch(f)λŠ” 문법이 κ°„κ²°ν•˜λ‹€λŠ” 점만 λΉΌκ³  then(null, f)κ³Ό μ™„λ²½ν•˜κ²Œ κ°™μŠ΅λ‹ˆλ‹€.

finally

try {...} catch {...}에 finally {...} 절이 μžˆλŠ” κ²ƒμ²˜λŸΌ, ν”„λΌλ―ΈμŠ€μ—λ„ finally()κ°€ μžˆμŠ΅λ‹ˆλ‹€.

ν”„λΌλ―ΈμŠ€κ°€ 처리되면(μ΄ν–‰μ΄λ‚˜ κ±°λΆ€) fκ°€ 항상 μ‹€ν–‰λœλ‹€λŠ” μ μ—μ„œ finally(f) ν˜ΈμΆœμ€ then(f, f)κ³Ό μœ μ‚¬ν•©λ‹ˆλ‹€.

μ“Έλͺ¨κ°€ 없어진 λ‘œλ”© 인디케이터(loading indicator)λ₯Ό λ©ˆμΆ”λŠ” κ²½μš°κ°™μ΄, κ²°κ³Όκ°€ μ–΄λ–»λ“  λ§ˆλ¬΄λ¦¬κ°€ ν•„μš”ν•˜λ©΄ finally()κ°€ μœ μš©ν•©λ‹ˆλ‹€.

그런데 finally()λŠ” then(f, f)κ³Ό μ™„μ „νžˆ 같진 μ•ŠμŠ΅λ‹ˆλ‹€. 차이점은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

finally() ν•Έλ“€λŸ¬μ—” μΈμžκ°€ μ—†μŠ΅λ‹ˆλ‹€. finally()에선 ν”„λΌλ―ΈμŠ€κ°€ μ΄ν–‰λ˜μ—ˆλŠ”μ§€, κ±°λΆ€λ˜μ—ˆλŠ”μ§€ μ•Œ 수 μ—†μŠ΅λ‹ˆλ‹€. finally()에선 절차λ₯Ό λ§ˆλ¬΄λ¦¬ν•˜λŠ” λ™μž‘μ„ μˆ˜ν–‰ν•˜κΈ° λ•Œλ¬Έμ— μ„±κ³΅Β·μ‹€νŒ¨ μ—¬λΆ€λ₯Ό λͺ°λΌλ„ λ©λ‹ˆλ‹€.

finally() ν•Έλ“€λŸ¬λŠ” μžλ™μœΌλ‘œ λ‹€μŒ ν•Έλ“€λŸ¬μ— 결과와 μ—λŸ¬λ₯Ό μ „λ‹¬ν•©λ‹ˆλ‹€.

resultκ°€ finally()λ₯Ό 거쳐 then()κΉŒμ§€ μ „λ‹¬λ˜λŠ” 것을 ν™•μΈν•΄λ΄…μ‹œλ‹€.

new Promise((resolve, reject) => {
  setTimeout(() => resolve("κ²°κ³Ό"), 2000)
})
  .finally(() => alert("ν”„λΌλ―ΈμŠ€κ°€ μ€€λΉ„λ˜μ—ˆμŠ΅λ‹ˆλ‹€."))
  .then(result => alert(result)); // <-- .thenμ—μ„œ resultλ₯Ό λ‹€λ£° 수 있음

ν”„λΌλ―ΈμŠ€μ—μ„œ μ—λŸ¬κ°€ λ°œμƒν•˜κ³  이 μ—λŸ¬κ°€ finally()λ₯Ό 거쳐 catch()κΉŒμ§€ μ „λ‹¬λ˜λŠ” 것을 ν™•μΈν•΄λ΄…μ‹œλ‹€.

new Promise((resolve, reject) => {
  throw new Error("μ—λŸ¬ λ°œμƒ!");
})
  .finally(() => alert("ν”„λΌλ―ΈμŠ€κ°€ μ€€λΉ„λ˜μ—ˆμŠ΅λ‹ˆλ‹€."))
  .catch(err => alert(err)); // <-- .catchμ—μ„œ μ—λŸ¬ 객체λ₯Ό λ‹€λ£° 수 있음

finally()λŠ” ν”„λΌλ―ΈμŠ€ κ²°κ³Όλ₯Ό μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄ λ§Œλ“€μ–΄ 진 게 μ•„λ‹™λ‹ˆλ‹€. ν”„λΌλ―ΈμŠ€ κ²°κ³ΌλŠ” finally()λ₯Ό ν†΅κ³Όν•΄μ„œ μ „λ‹¬λ©λ‹ˆλ‹€. 이런 νŠΉμ§•μ€ μ•„μ£Ό μœ μš©ν•˜κ²Œ μ‚¬μš©λ˜κΈ°λ„ ν•©λ‹ˆλ‹€.

finally(f)λŠ” ν•¨μˆ˜ fλ₯Ό μ€‘λ³΅ν•΄μ„œ μ“Έ ν•„μš”κ°€ μ—†κΈ° λ•Œλ¬Έμ— then(f, f)보닀 문법 μΈ‘λ©΄μ—μ„œ 더 νŽΈλ¦¬ν•©λ‹ˆλ‹€.

profile
생각 많이 ν•˜μ§€ μ•ŠκΈ° 😎
post-custom-banner

0개의 λŒ“κΈ€