Skip to content

Latest commit

Β 

History

History

37-Promise

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
Β 
Β 

ES6μ—μ„œ 비동기 처리λ₯Ό μœ„ν•œ 또 λ‹€λ₯Έ νŒ¨ν„΄μœΌλ‘œ ν”„λ‘œλ―ΈμŠ€(Promise)λ₯Ό λ„μž…

  • ν”„λ‘œλ―ΈμŠ€λŠ” 전톡적인 콜백 νŒ¨ν„΄μ΄ 가진 μ½œλ°±μ§€μ˜₯의 가독성 λ¬Έμ œμ™€ μ—λŸ¬ μ²˜λ¦¬κ°€ κ³€λž€ν•˜λ‹€λŠ” 단점을 보완
  • 비동기 처리 μ‹œμ μ„ λͺ…ν™•ν•˜κ²Œ ν‘œν˜„ν•  수 μžˆλ‹€λŠ” μž₯점이 μžˆλ‹€.


비동기 처리 - κΈ°μ‘΄ 콜백 νŒ¨ν„΄μ˜ 단점

비동기 ν•¨μˆ˜ λ‚΄λΆ€μ˜ λΉ„λ™κΈ°λ‘œ λ™μž‘ν•˜λŠ” μ½”λ“œμ—μ„œ 처리 κ²°κ³Όλ₯Ό μ™ΈλΆ€λ‘œ λ°˜ν™˜ν•˜κ±°λ‚˜ μƒμœ„ μŠ€μ½”ν”„μ˜ λ³€μˆ˜μ— ν• λ‹Ήν•˜λ©΄ κΈ°λŒ€ν•œ λŒ€λ‘œ λ™μž‘ν•˜μ§€ μ•ŠλŠ”λ‹€.

  • 비동기 ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ ν•¨μˆ˜ λ‚΄λΆ€μ˜ λΉ„λ™κΈ°λ‘œ λ™μž‘ν•˜λŠ” μ½”λ“œκ°€ μ™„λ£Œλ˜μ§€ μ•Šμ•˜λ‹€ 해도 기닀리지 μ•Šκ³  μ¦‰μ‹œ μ’…λ£Œλ˜κΈ° λ•Œλ¬Έ
let g = 0;

// setTimeout 비동기 ν•¨μˆ˜μ˜ 콜백 ν•¨μˆ˜μ—μ„œ μ „μ—­ λ³€μˆ˜ g의 값이 λ³€κ²½λ˜κΈΈ κΈ°λŒ€
// μ‹€μ œλ‘œλŠ” λ³€μˆ˜ g의 값은 λ³€κ²½λ˜μ§€ μ•ŠλŠ”λ‹€.
// setTimeout 의 μ½œλ°±ν•¨μˆ˜κ°€ 콜 μŠ€νƒμœΌλ‘œ μ΄λ™ν•΄μ„œ μ‹€ν–‰λ˜λŠ” μ‹œμ μ—λŠ” 이미 console.log ν•¨μˆ˜κ°€ λλ‚œ μ‹œμ μ΄κΈ° λ•Œλ¬Έ
setTimeout(() => {
  g = 100;
}, 0);

console.log(g); // 0

λ”°λΌμ„œ, 비동기 ν•¨μˆ˜μ˜ 처리 κ²°κ³Ό(λŒ€ν‘œμ μœΌλ‘œ, μ„œλ²„μ˜ 응닡 λ“±)에 λŒ€ν•œ 후속 μ²˜λ¦¬λŠ” 비동기 ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ μˆ˜ν–‰ν•΄μ•Ό ν•œλ‹€.

  • μ΄λ•Œ 후속 처리λ₯Ό μœ„ν•œ 비동기 ν•¨μˆ˜λ₯Ό 콜백 ν•¨μˆ˜λ‘œ μ „λ‹¬ν•˜λŠ” 것이 일반적
  • ν•„μš”μ— 따라, 비동기 μ²˜λ¦¬κ°€ μ„±κ³΅ν•˜λ©΄ 호좜될 콜백 ν•¨μˆ˜μ™€ 비동기 μ²˜λ¦¬κ°€ μ‹€νŒ¨ν•˜λ©΄ 호좜될 콜백 ν•¨μˆ˜λ₯Ό μ „λ‹¬ν•˜λŠ” 것
// GET μš”μ²­μ„ μœ„ν•œ 비동기 ν•¨μˆ˜
// μš”μ²­ 결과에 따라 μˆ˜ν–‰ν•  성곡,μ‹€νŒ¨ 콜백 ν•¨μˆ˜ 전달
const get = (url, successCallback, failureCallback) => {
  const xhr = new XMLHttpRequest();
  xhr.open();
  xhr.send("GET", url);

  xhr.onload = () => {
    if (xhr.status === 200) {
      successCallback(JSON.parse(xhr.response));
    } else {
      failureCallback(xhr.status);
    }
  };
};

get("https://jsonplaceholder.typicode.com/posts/1", console.log, console.error);
// {
//   userId: 1,
//   id: 1,
//   title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',
//   body: 'quia et suscipit\nsuscipit recusandae consequuntur …strum rerum est autem sunt rem eveniet architecto'
// };

λ‹€λ§Œ, μ΄λŸ°μ‹μœΌλ‘œ 후속 μ²˜λ¦¬μ— ν•„μš”ν•œ 비동기 ν•¨μˆ˜λ₯Ό 콜백 ν•¨μˆ˜λ‘œ μ „λ‹¬ν•˜κ³  ν˜ΈμΆœν•˜λŠ” 방식이 μ€‘μ²©λ˜λ©΄ λ³΅μž‘λ„κ°€ λ†’μ•„μ§€λŠ” ν˜„μƒμ΄ λ°œμƒ β†’ μ½œλ°±μ§€μ˜₯(callback hell)

...

get("/step1", (a) => {
  get(`/step2/${a}`, (b) => {
    get(`/step3/${b}`, (c) => {
      get(`/step4/${c}`, (d) => {
        console.log(d);
      });
    });
  });
});

λ˜ν•œ, 콜백 νŒ¨ν„΄μ˜ 문제점 쀑 κ°€μž₯ μ‹¬κ°ν•œ 것은 μ—λŸ¬ μ²˜λ¦¬κ°€ κ³€λž€ν•˜λ‹€λŠ” 것

  • μ—λŸ¬λŠ” 호좜자(caller)λ°©ν–₯으둜 μ „νŒŒλœλ‹€.
    • 즉, ν˜„μž¬ μ‹€ν–‰ 쀑인 μ‹€ν–‰ μ»¨ν…μŠ€νŠΈκ°€ ν‘Έμ‹œλ˜κΈ° 직전에 ν‘Έμ‹œλœ μ‹€ν–‰ μ»¨ν…μŠ€νŠΈ λ°©ν–₯으둜 μ „νŒŒλœλ‹€.
try {
  setTimeout(() => {
    throw new Error("Error!");
  }, 1000);
} catch (error) {
  // 이벀트 νμ—μ„œ 콜 μŠ€νƒμœΌλ‘œ setTimeout 의 콜백 ν•¨μˆ˜λ‘œ μ΄λ™λ˜μ–΄ μ‹€ν–‰λ˜λŠ” μ‹œμ μ—μ„œ setTimout ν•¨μˆ˜λŠ” 이미, 콜 μŠ€νƒμ—μ„œ pop된 μƒνƒœμ΄κ³  주체가 μ•„λ‹ˆλ‹€.
  // 콜백 ν•¨μˆ˜μ˜ 호좜자(caller)κ°€ setTimeout 인데, μ—λŸ¬ λ°œμƒ μ‹œμ μ— μ‘΄μž¬ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— Error λ₯Ό μΊμΉ˜ν•  수 μ—†λ‹€.
  console.error("캐치 μ—λŸ¬ :", error);
}


ν”„λ‘œλ―ΈμŠ€ 생성

Promise μƒμ„±μž ν•¨μˆ˜λ₯Ό new ν‚€μ›Œλ“œμ™€ ν•¨κ»˜ ν˜ΈμΆœν•˜λ―€λ‘œμ¨ ν”„λ‘œλ―ΈμŠ€(Promise 객체)λ₯Ό 생성

  • ν”„λ‘œλ―ΈμŠ€λŠ” 호슀트 객체가 μ•„λ‹Œ ECAMScript 사양에 μ •μ˜λœ ν‘œμ€€ 빌트인 객체
  • Promise μƒμ„±μž ν•¨μˆ˜λŠ” 비동기 처리λ₯Ό μˆ˜ν–‰ν•  콜백 ν•¨μˆ˜λ₯Ό 인수둜 전달 λ°›λŠ”λ‹€.
    • resolve 와 reject ν•¨μˆ˜λ₯Ό 인수둜 전달
    const promise = new Promise((resovle, reject) => {
      if(비동기 처리 성곡) {
        resolve('result');
      } else {
        reject('failure Error');
      }
    })
  • ν”„λ‘œλ―ΈμŠ€λŠ” ν˜„μž¬ 비동기 μ²˜λ¦¬κ°€ μ–΄λ–»κ²Œ μ§„ν–‰λ˜κ³  μžˆλŠ”μ§€λ₯Ό λ‚˜νƒ€λ‚΄λŠ” μƒνƒœ(state)정보λ₯Ό 가진닀.
    ν”„λ‘œλ―ΈμŠ€ μƒνƒœ 정보 의미 μƒνƒœ λ³€κ²½ 쑰건
    pending 비동기 μ²˜λ¦¬κ°€ 아직 μˆ˜ν–‰λ˜μ§€ μ•Šμ€ μƒνƒœ ν”„λ‘œλ―ΈμŠ€κ°€ μƒμ„±λœ 직후 κΈ°λ³Έ μƒνƒœ
    fulfilled 비동기 μ²˜λ¦¬κ°€ μˆ˜ν–‰λœ μƒνƒœ( 성곡 ) resolve ν•¨μˆ˜ 호좜
    rejected 비동기 μ²˜λ¦¬κ°€ μˆ˜ν–‰λœ μƒνƒœ( μ‹€νŒ¨ ) reject ν•¨μˆ˜ 호좜
    • κΈ°λ³Έμ μœΌλ‘œλŠ” pending μƒνƒœμ΄λ©°, 이후 비동기 μ²˜λ¦¬κ°€ μˆ˜ν–‰λ˜λ©΄ 비동기 처리 결과에 따라 ν”„λ‘œλ―ΈμŠ€ μƒνƒœκ°€ λ³€κ²½
      • 비동기 처리 성곡 β†’ resolve ν•¨μˆ˜λ₯Ό 호좜 β†’ ν”„λ‘œλ―ΈμŠ€λ₯Ό fulfilled μƒνƒœλ‘œ λ³€κ²½
      • 비동기 처리 μ‹€νŒ¨ β†’ reject ν•¨μˆ˜λ₯Ό 호좜 β†’ ν”„λ‘œλ―ΈμŠ€λ₯Ό rejected μƒνƒœλ‘œ λ³€κ²½
    • 즉, ν”„λ‘œλ―ΈμŠ€ μƒνƒœλŠ” resolve λ˜λŠ” reject ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λŠ” κ²ƒμœΌλ‘œ κ²°μ •
    • rejected λ˜λŠ” fulfilled μƒνƒœλ₯Ό settled μƒνƒœλΌκ³  ν•œλ‹€.
      • 즉, settled μƒνƒœλŠ” 비동기 μ²˜λ¦¬κ°€ μˆ˜ν–‰λœ μƒνƒœλ₯Ό 의미
      • ν•œ 번 settled μƒνƒœκ°€ 되면 λ”λŠ” λ‹€λ₯Έ μƒνƒœλ‘œ λ³€ν™”ν•  수 μ—†λ‹€.
  • ν”„λ‘œλ―ΈμŠ€λŠ” 비동기 처리 μƒνƒœμ™€ 비동기 처리 결과도 μƒνƒœλ‘œ κ°–λŠ”λ‹€.
    const fulfilled = new Promise((resolve) => resolve(1));
    console.log(fulfilled);
    /*
    [[Prototype]]: Promise
    [[PromiseState]]: "fulfilled"
    [[PromiseResult]]: 1
    */
    
    const rejected = new Promise((_, reject) => reject(new Error("failure Error")));
    console.log(rejected);
    /*
    [[Prototype]]: Promise
    [[PromiseState]]: "rejected"
    [[PromiseResult]]: Error: failure Error
    */


ν”„λ‘œλ―ΈμŠ€ 후속 처리 λ©”μ„œλ“œ

ν”„λ‘œλ―ΈμŠ€μ˜ 비동기 처리 μƒνƒœκ°€ λ³€κ²½λ˜λ©΄, 이에 λ”°λ₯Έ 후속 처리λ₯Ό ν•΄μ•Ό ν•œλ‹€.

  • ν”„λ‘œλ―ΈμŠ€κ°€ fulfilled μƒνƒœκ°€ 되면 β†’ ν”„λ‘œλ―ΈμŠ€μ˜ 처리 κ²°κ³Όλ₯Ό 가지고 무언가λ₯Ό μˆ˜ν–‰
  • ν”„λ‘œλ―ΈμŠ€κ°€ rejected μƒνƒœκ°€ 되면 β†’ ν”„λ‘œλ―ΈμŠ€μ˜ 처리 κ²°κ³Ό(μ—λŸ¬)λ₯Ό 가지고 μ—λŸ¬ 처리λ₯Ό μˆ˜ν–‰

ν”„λ‘œλ―ΈμŠ€μ˜ 비동기 처리 μƒνƒœκ°€ λ³€ν™”ν•˜λ©΄, 후속 처리 λ©”μ„œλ“œμ— 인수둜 μ „λ‹¬ν•œ 콜백 ν•¨μˆ˜κ°€ μ„ νƒμ μœΌλ‘œ ν˜ΈμΆœλœλ‹€.

  • 이λ₯Ό μœ„ν•΄, ν”„λ‘œλ―ΈμŠ€λŠ” 후속 처리 λ©”μ„œλ“œ then, catch, finally λ₯Ό 제곡

Promise.prototype.then

두 개의 콜백 ν•¨μˆ˜λ₯Ό 인수둜 μ „λ‹¬λ°›λŠ”λ‹€.

  • 첫 번째 콜백 ν•¨μˆ˜ β†’ ν”„λ‘œλ―ΈμŠ€κ°€ fulfilled μƒνƒœ(resolve ν•¨μˆ˜κ°€ 호좜된 μƒνƒœ)κ°€ 되면 호좜, 콜백 ν•¨μˆ˜λŠ” ν”„λ‘œλ―ΈμŠ€μ˜ 비동기 처리 κ²°κ³Όλ₯Ό 인수둜 μ „λ‹¬λ°›μŒ
  • 두 번째 콜백 ν•¨μˆ˜ β†’ ν”„λ‘œλ―ΈμŠ€κ°€ rejected μƒνƒœ(reject ν•¨μˆ˜κ°€ 호좜된 μƒνƒœ)κ°€ 되면 호좜, 콜백 ν•¨μˆ˜λŠ” ν”„λ‘œλ―ΈμŠ€μ˜ μ—λŸ¬λ₯Ό 인수둜 μ „λ‹¬λ°›μŒ
new Promise((resolve) => resolve("fulfilled")).then(
  (v) => console.log(v),
  (e) => console.error(e),
);
// fulfilled

new Promise((_, reject) => reject(new Error("rejected"))).then(
  (v) => console.log(v),
  (e) => console.error(e),
);
// Error: rejected
  • then λ©”μ„œλ“œλŠ” μ–Έμ œλ‚˜ ν”„λ‘œλ―ΈμŠ€λ₯Ό λ°˜ν™˜
    • then λ©”μ„œλ“œμ˜ 콜백 ν•¨μˆ˜κ°€ ν”„λ‘œλ―ΈμŠ€λ₯Ό λ°˜ν™˜ν•˜λ©΄ κ·Έ ν”„λ‘œλ―ΈμŠ€λ₯Ό κ·ΈλŒ€λ‘œ λ°˜ν™˜
    • 값을 λ°˜ν™˜ν•˜λ©΄ κ·Έ 값을 μ•”λ¬΅μ μœΌλ‘œ resolve λ˜λŠ” reject ν•˜μ—¬ ν”„λ‘œλ―ΈμŠ€λ₯Ό 생성해 λ°˜ν™˜

Promise.prototype.catch

ν•œ 개의 콜백 ν•¨μˆ˜λ₯Ό 인수둜 μ „λ‹¬λ°›λŠ”λ‹€.

  • ν”„λ‘œλ―ΈμŠ€κ°€ rejected μƒνƒœ(reject ν•¨μˆ˜κ°€ 호좜된 μƒνƒœ)인 경우만 호좜
  • catch λ©”μ„œλ“œλŠ” then(undefined, onRejected) κ³Ό λ™μΌν•˜κ²Œ λ™μž‘
  • catch λ©”μ„œλ“œλ„ μ–Έμ œλ‚˜ ν”„λ‘œλ―ΈμŠ€λ₯Ό λ°˜ν™˜
new Promise((_, reject) => reject(new Error("rejected"))).catch((e) => console.log(e)); // Error: rejected

// μœ„μ— catch λ¬Έκ³Ό 같은 κΈ°λŠ₯을 ν•˜λŠ” then 버전
new Promise((_, reject) => reject(new Error("rejected"))).then(undefined, (e) => console.log(e)); // Error: rejected

Promise.prototype.finally

ν•œ 개의 콜백 ν•¨μˆ˜λ₯Ό 인수둜 μ „λ‹¬λ°›λŠ”λ‹€.

  • ν”„λ‘œλ―ΈμŠ€μ˜ 성곡 λ˜λŠ” μ‹€νŒ¨μ™€ 상관없이 무쑰건 ν•œ 번 ν˜ΈμΆœλœλ‹€.
  • ν”„λ‘œλ―ΈμŠ€μ˜ μƒνƒœμ™€ 상관없이 κ³΅ν†΅μ μœΌλ‘œ μˆ˜ν–‰ν•΄μ•Ό ν•  처리 λ‚΄μš©μ΄ μžˆμ„ λ•Œ 유용
  • finally λ©”μ„œλ“œλ„ μ–Έμ œλ‚˜ ν”„λ‘œλ―ΈμŠ€λ₯Ό λ°˜ν™˜
const promiseGET = (url) => {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.send();

    xhr.onload = () => {
      if (xhr.status === 200) {
        resolve(JSON.parse(xhr.response));
      } else {
        reject(new Error(xhr.status));
      }
    };
  });
};

promiseGET("https://jsonplaceholder.typicode.com/posts/1")
  .then((res) => console.log(res))
  .catch((err) => console.log(err))
  .finally(() => console.log("End !"));

// {
//   userId: 1,
//   id: 1,
//   title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',
//   body: 'quia et suscipit\nsuscipit recusandae consequuntur …strum rerum est autem sunt rem eveniet architecto'
// }
// End !


ν”„λ‘œλ―ΈμŠ€μ˜ μ—λŸ¬ 처리

두 가지 방법이 μžˆλ‹€.

  • then λ©”μ„œλ“œμ˜ 두 번째 콜백 ν•¨μˆ˜λ‘œ μ²˜λ¦¬ν•˜λŠ” 방법
    • then λ©”μ„œλ“œλŠ” 두 개의 콜백 ν•¨μˆ˜λ₯Ό 전달 λ°›λŠ”λ‹€κ³  ν–ˆλ‹€.
    • 즉, then(성곡 μ‹œ, μ‹€νŒ¨ μ‹œ)λ₯Ό μ˜λ―Έν–ˆλ‹€.
    • λ”°λΌμ„œ, then(_ , onRejected)의 ν˜•νƒœλ‘œ ν˜ΈμΆœν•˜λ©΄ μ—λŸ¬ 처리 κ°€λŠ₯
    const promiseGET = (url) => { ... }
    
    const wrongURL = "https://wrongURL.com";
    // then λ©”μ„œλ“œ 두 번째 νŒŒλΌλ―Έν„°λ‘œ μ—λŸ¬ 캐칭 λ©”μ„œλ“œλ₯Ό λ„˜κΈ°λŠ” 방식
    promiseGET(wrongURL).then(
      (res) => console.log(res),
      (err) => console.log(err),
    );
  • catch λ©”μ„œλ“œλ‘œ μ²˜λ¦¬ν•˜λŠ” 방법
    const promiseGET = (url) => { ... }
    
    // catch λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” 방식
    promiseGET(wrongURL)
      .then((res) => console.log(res))
      .catch((err) => console.log(err));
    
    // catch λ©”μ„œλ“œλŠ” λ‚΄λΆ€μ μœΌλ‘œ λ‹€μŒκ³Ό κ°™λ‹€.
    promiseGET(wrongURL)
      .then((res) => console.log(res))
      .then(undefined, (err) => console.log(err));

λ‹€λ§Œ, 일반적으둜 ν”„λ‘œλ―ΈμŠ€μ˜ μ—λŸ¬ μ²˜λ¦¬λŠ” catch λ©”μ„œλ“œλ‘œ μ²˜λ¦¬ν•œλ‹€.

  • then λ©”μ„œλ“œμ˜ 두 번째 인수둜 μ—λŸ¬ 처리 콜백 ν•¨μˆ˜μ—μ„œ λ°œμƒν•œ μ—λŸ¬λŠ” μΊμΉ˜ν•˜μ§€ λͺ»ν•˜κ³  μ½”λ“œκ°€ λ³΅μž‘ν•΄μ Έ 가독성도 쒋지 μ•Šλ‹€λŠ” 단점이 μ‘΄μž¬ν•˜κΈ° λ•Œλ¬Έ


ν”„λ‘œλ―ΈμŠ€ 체이닝

ν”„λ‘œλ―ΈμŠ€ 체이닝(Promise chainning) : then, catch, finally 후속 처리 λ©”μ„œλ“œλŠ” μ–Έμ œλ‚˜ ν”„λ‘œλ―ΈμŠ€(Promise 객체)λ₯Ό λ°˜ν™˜ν•˜κΈ° λ•Œλ¬Έμ—, μ—°μ†μ μœΌλ‘œ 호좜(체이닝)이 κ°€λŠ₯

  • ν”„λ‘œλ―ΈμŠ€λŠ” ν”„λ‘œλ―ΈμŠ€ 체이닝을 톡해 비동기 처리 κ²°κ³Όλ₯Ό 전달받아 후속 처리λ₯Ό ν•˜λ―€λ‘œ 비동기 처리λ₯Ό μœ„ν•œ 콜백 νŒ¨ν„΄μ˜ λ¬Έμ œμ˜€λ˜ μ½œλ°±μ§€μ˜₯이 λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€.
  • λ‹€λ§Œ, ν”„λ‘œλ―ΈμŠ€λ„ 콜백 νŒ¨ν„΄μ„ μ‚¬μš©ν•˜λ―€λ‘œ 콜백 ν•¨μˆ˜λ₯Ό μ•„μ˜ˆ μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” 것은 μ•„λ‹ˆλ‹€.

콜백 νŒ¨ν„΄μ€ 가독성이 쒋지 μ•Šλ‹€.

  • 이 문제λ₯Ό ES8에 λ„μž…λœ async / await λ₯Ό 톡해 ν•΄κ²° κ°€λŠ₯ν•˜λ‹€.
    • ν”„λ‘œλ―ΈμŠ€μ˜ 후속 처리 λ©”μ„œλ“œ 없이 마치 동기 처리처럼 ν”„λ‘œλ―ΈμŠ€κ°€ 처리 κ²°κ³Όλ₯Ό λ°˜ν™˜ν•˜λ„λ‘ κ΅¬ν˜„ κ°€λŠ₯
    const promiseGET = (url) => { ... }
    const url = "https://jsonplaceholder.typicode.com";
    
    (async () => {
      const { userId } = await promiseGET(`${url}/posts/1`);
      const userInfo = await promiseGET(`${url}/users${userId}`);
      console.log(userInfo);
    })();


ν”„λ‘œλ―ΈμŠ€ 정적 λ©”μ„œλ“œ

Promise λŠ” μƒμ„±μž ν•¨μˆ˜, 즉 ν•¨μˆ˜λ„ κ°μ²΄μ΄λ―€λ‘œ λ©”μ„œλ“œλ₯Ό 가진닀.

  • 5가지 정적 λ©”μ„œλ“œλ₯Ό 제곡

Promise.resolve / Promise.reject 정적 λ©”μ„œλ“œ

이미 μ‘΄μž¬ν•˜λŠ” 값을 λž˜ν•‘ν•˜μ—¬ ν”„λ‘œλ―ΈμŠ€λ₯Ό μƒμ„±ν•˜κΈ° μœ„ν•΄ μ‚¬μš©

  • Promise.resolve β†’ 인수둜 전달받은 값을 resolve ν•˜λŠ” ν”„λ‘œλ―ΈμŠ€λ₯Ό 생성
    const resolvedPromise = Promise.resolve([1, 2, 3]);
    // const resolvedPromise = new Promise((resolve) => resolve([1, 2, 3]));
    
    resolvedPromise.then((v) => console.log(v)); // [ 1, 2, 3 ]
  • Promise.reject β†’ 인수둜 전달받은 κ°’(μ—λŸ¬)을 reject ν•˜λŠ” ν”„λ‘œλ―ΈμŠ€λ₯Ό 생성
    const rejectedPromise = Promise.reject(new Error("Error !"));
    // const rejectedPromise = new Promise((_, reject) => reject(new Error("Error !")));
    
    rejectedPromise.catch((err) => console.log(err)); // Error: Error !

Promise.all 정적 λ©”μ„œλ“œ

μ—¬λŸ¬ 개의 비동기 처리λ₯Ό λͺ¨λ‘ 병렬(parallel)μ²˜λ¦¬ν•  λ•Œ μ‚¬μš©

  • μ„œλ‘œ 독립적인 μ—¬λŸ¬ 비동기 처리λ₯Ό Promise.prototype.then 후속 처리 λ©”μ„œλ“œλ‘œ 체이닝 ν•˜λŠ” 경우
    const req1 = () => new Promise((resolve) => setTimeout(() => resolve(1), 3000));
    const req2 = () => new Promise((resolve) => setTimeout(() => resolve(2), 2000));
    const req3 = () => new Promise((resolve) => setTimeout(() => resolve(3), 1000));
    
    const res = [];
    req1()
      .then((data) => {
        res.push(data);
        return req2();
      })
      .then((data) => {
        res.push(data);
        return req3();
      })
      .then((data) => {
        res.push(data);
        console.log(res);
      })
      .catch((err) => console.log(err));
    
    // μ•½ 6초 λ’€..
    // [ 1, 2, 3 ]
  • μ„œλ‘œ 독립적인 μ—¬λŸ¬ 비동기 처리λ₯Ό Promise.all 정적 λ©”μ„œλ“œλ‘œ μ²˜λ¦¬ν•˜λŠ” 경우
    const req1 = () => new Promise((resolve) => setTimeout(() => resolve(1), 3000));
    const req2 = () => new Promise((resolve) => setTimeout(() => resolve(2), 2000));
    const req3 = () => new Promise((resolve) => setTimeout(() => resolve(3), 1000));
    
    Promise.all([req1(), req2(), req3()])
      .then((res) => console.log(res))
      .catch((err) => console.log(err));
    
    // μ•½ 3초 λ’€.. ( κ°€μž₯ 였래 κ±Έλ¦¬λŠ” 비동기 μ²˜λ¦¬κ°€ 3000ms(μ•½ 3초))
    // [ 1, 2, 3 ]
  • Promise.all λ©”μ„œλ“œλŠ” ν”„λ‘œλ―ΈμŠ€λ₯Ό μš”μ†Œλ‘œ κ°–λŠ” λ°°μ—΄ λ“±μ˜ μ΄ν„°λŸ¬λΈ”μ„ 인수둜 μ „λ‹¬λ°›λŠ”λ‹€.
  • 전달받은 λͺ¨λ“  ν”„λ‘œλ―ΈμŠ€κ°€ λͺ¨λ‘ fulfilled μƒνƒœκ°€ 되면 λͺ¨λ“  처리 κ²°κ³Όλ₯Ό 배열에 μ €μž₯ν•΄ μƒˆλ‘œμš΄ ν”„λ‘œλ―ΈμŠ€λ₯Ό λ°˜ν™˜
    • Promise.all λ©”μ„œλ“œκ°€ μ’…λ£Œν•˜λŠ” 데 κ±Έλ¦¬λŠ” μ‹œκ°„ = κ°€μž₯ 늦게 fulfilled μƒνƒœκ°€ λ˜λŠ” ν”„λ‘œλ―ΈμŠ€μ˜ 처리 μ‹œκ°„ + a μ‹œκ°„
    • μˆœμ„œκ°€ 보μž₯λœλ‹€.
      • κ°€μž₯ 늦게 fulfilled λ˜λŠ” ν”„λ‘œλ―ΈμŠ€κ°€ λ°°μ—΄ μš”μ†Œμ— μ•žμ— μžˆμ–΄λ„, μˆœμ„œλŒ€λ‘œ 배열에 μ €μž₯ ν›„ μƒˆλ‘œμš΄ ν”„λ‘œλ―ΈμŠ€λ₯Ό λ°˜ν™˜
  • 전달받은 ν”„λ‘œλ―ΈμŠ€ μš”μ†Œλ“€ 쀑, ν•˜λ‚˜λΌλ„ rejected μƒνƒœκ°€ 되면 λ‚˜λ¨Έμ§€ ν”„λ‘œλ―ΈμŠ€μ˜ 진행 μƒνƒœμ™€ 상관없이 기닀리지 μ•Šκ³  μ¦‰μ‹œ μ’…λ£Œ
    const req1 = () => new Promise((_, reject) => setTimeout(() => reject(new Error("Error 1")), 3000));
    const req2 = () => new Promise((_, reject) => setTimeout(() => reject(new Error("Error 2")), 2000));
    const req3 = () => new Promise((_, reject) => setTimeout(() => reject(new Error("Error 3")), 1000));
    
    Promise.all([req1(), req2(), req3()])
      .then((res) => console.log(res))
      .catch((err) => console.log(err));
    
    // μ•½ 1초 λ’€..
    // Error: Error 3 << λͺ¨λ“  ν”„λ‘œλ―ΈμŠ€ μž‘μ—… 쀑 κ°€μž₯ λΉ λ₯΄κ²Œ rejected λ˜λŠ” ν”„λ‘œλ―ΈμŠ€μ˜ reject ν•¨μˆ˜λ₯Ό 호좜
  • 전달받은 μ΄ν„°λŸ¬λΈ”μ˜ μš”μ†Œλ“€μ΄ ν”„λ‘œλ―ΈμŠ€κ°€ μ•„λ‹Œ 값인 경우, Promise.resolve λ©”μ„œλ“œλ₯Ό 톡해 ν”„λ‘œλ―ΈμŠ€λ‘œ λž˜ν•‘ν•œλ‹€.
    Promise.all([1, 2, 3])
      .then((res) => console.log(res))
      .catch((err) => console.log);
    
    // [ 1, 2, 3 ]

Promise.race 정적 λ©”μ„œλ“œ

Promise.all λ©”μ„œλ“œμ™€ λ™μΌν•˜κ²Œ ν”„λ‘œλ―ΈμŠ€λ₯Ό μš”μ†Œλ‘œ κ°–λŠ” λ°°μ—΄ λ“±μ˜ μ΄ν„°λŸ¬λΈ”μ„ 인수둜 μ „λ‹¬λ°›λŠ”λ‹€.

  • λ‹€λ§Œ, Promise.all λ©”μ„œλ“œμ²˜λŸΌ λͺ¨λ“  ν”„λ‘œλ―ΈμŠ€ μš”μ†Œλ“€μ΄ fulfilled 될 λ•ŒκΉŒμ§€ 기닀리지 μ•Šκ³ , κ°€μž₯ λ¨Όμ € fulfilled μƒνƒœκ°€ λ˜λŠ” ν”„λ‘œλ―ΈμŠ€μ˜ 처리 κ²°κ³Όλ₯Ό resolve ν•˜λŠ” μƒˆλ‘œμš΄ ν”„λ‘œλ―ΈμŠ€λ₯Ό λ°˜ν™˜
const req1 = () => new Promise((resolve) => setTimeout(() => resolve(1), 3000));
const req2 = () => new Promise((resolve) => setTimeout(() => resolve(2), 2000));
const req3 = () => new Promise((resolve) => setTimeout(() => resolve(3), 1000));

Promise.race([req1(), req2(), req3()])
  .then((res) => console.log(res))
  .catch((err) => console.log(err));

// μ•½ 1초 λ’€.. ( κ°€μž₯ λ¨Όμ € fulfilled μƒνƒœκ°€ λ˜λŠ” ν”„λ‘œλ―ΈμŠ€ 처리 κ²°κ³Όλ₯Ό resolve ν•¨μˆ˜ 호좜둜 ν”„λ‘œλ―ΈμŠ€ λ°˜ν™˜ )
// 3
// λ‚˜λ¨Έμ§€ μ•½ 2초 λ’€, μ’…λ£Œ ( κ°€μž₯ 였래 κ±Έλ¦¬λŠ” ν”„λ‘œλ―ΈμŠ€ κΈ°μ€€ )

Promise.allSetteld 정적 λ©”μ„œλ“œ

ES11에 λ„μž…, 전달받은 ν”„λ‘œλ―ΈμŠ€κ°€ λͺ¨λ‘ settled μƒνƒœ(= 비동기 μ²˜λ¦¬κ°€ μˆ˜ν–‰λœ μƒνƒœ)κ°€ 되면, 처리 κ²°κ³Όλ₯Ό λ°°μ—΄λ‘œ λ°˜ν™˜

  • ν”„λ‘œλ―ΈμŠ€κ°€ fulfilled μƒνƒœμΈ 경우 β†’ { status, value } ν”„λ‘œνΌν‹°λ₯Ό κ°–λŠ”λ‹€.
  • ν”„λ‘œλ―ΈμŠ€κ°€ rejected μƒνƒœμΈ 경우 β†’ { status, reason } ν”„λ‘œνΌν‹°λ₯Ό κ°–λŠ”λ‹€.
  • ν”„λ‘œλ―ΈμŠ€λ₯Ό μš”μ†Œλ‘œ κ°–λŠ” λ°°μ—΄ λ“±μ˜ μ΄ν„°λŸ¬λΈ”μ„ 인수둜 μ „λ‹¬λ°›λŠ”λ‹€.
const resolvedPromise = new Promise((resolve) => setTimeout(() => resolve(1), 2000));
const rejectedPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("Error !"), 1000)));

Promise.allSettled([resolvedPromise, rejectedPromise]).then((res) => console.log(res));
// [
//   { status: 'fulfilled', value: 1 },
//   {
//     status: 'rejected',
//     reason: Error: Error !
//         at Timeout._onTimeout (C:\Users\forze\Desktop\Docs-modernJS__deepDive\37-Promise\index.js:2:76)
//         at listOnTimeout (node:internal/timers:556:17)
//         at processTimers (node:internal/timers:499:7)
//   }
// ]


λ§ˆμ΄ν¬λ‘œνƒœμŠ€ν¬ 큐

setTimeout(() => console.log(1), 0);

Promise.resolve()
  .then(() => console.log(2))
  .then(() => console.log(3));

// 2 3 1
  • μœ„μ— 예제의 κ²°κ³ΌλŠ” 1 β†’ 2 β†’ 3 이 μ•„λ‹Œ, 2 β†’ 3 β†’ 1 순으둜 좜λ ₯λœλ‹€.

κ·Έ μ΄μœ λŠ”, ν”„λ‘œλ―ΈμŠ€ 후속 처리 λ©”μ„œλ“œμ˜ 콜백 ν•¨μˆ˜λŠ” νƒœμŠ€ν¬ 큐(task queue, = event queue)κ°€ μ•„λ‹Œ λ§ˆμ΄ν¬λ‘œνƒœμŠ€ν¬ 큐(microtask queue, = job queue)에 μ €μž₯λœλ‹€.

  • λ§ˆμ΄ν¬λ‘œνƒœμŠ€ν¬ νλŠ” νƒœμŠ€ν¬ νμ™€λŠ” λΈŒλΌμš°μ €μ—μ„œ μ œκ³΅ν•˜λŠ” λ³„λ„μ˜ 큐닀.
    • λ§ˆμ΄ν¬λ‘œνƒœμŠ€ν¬ 큐(= 작 큐) β†’ ν”„λ‘œλ―ΈμŠ€ 후속 처리 λ©”μ„œλ“œμ˜ 콜백 ν•¨μˆ˜κ°€ μΌμ‹œμ μœΌλ‘œ 보관
    • νƒœμŠ€ν¬ 큐(= 이벀트 큐) β†’ κ·Έ μ™Έμ˜ 비동기 ν•¨μˆ˜μ˜ 콜백 ν•¨μˆ˜λ‚˜ 이벀트 ν•Έλ“€λŸ¬κ°€ μΌμ‹œμ μœΌλ‘œ 보관
  • μš°μ„  μˆœμœ„ = λ§ˆμ΄ν¬λ‘œνƒœμŠ€ν¬ 큐 > νƒœμŠ€ν¬ 큐
    • 즉, 이벀트 λ£¨ν”„λŠ” 콜 μŠ€νƒμ΄ λΉ„λ©΄ λ¨Όμ € λ§ˆμ΄ν¬λ‘œνƒœμŠ€ν¬ νμ—μ„œ λŒ€κΈ°ν•˜κ³  μžˆλŠ” ν•¨μˆ˜λ₯Ό 가져와 μ‹€ν–‰
    • 이후 λ§ˆμ΄ν¬λ‘œνƒœμŠ€ν¬ 큐가 λΉ„λ©΄ νƒœμŠ€ν¬ νμ—μ„œ λŒ€κΈ°ν•˜κ³  μžˆλŠ” ν•¨μˆ˜λ₯Ό 가져와 μ‹€ν–‰


fetch

XMLHttpRequest 객체와 λ§ˆμ°¬κ°€μ§€λ‘œ HTTP μš”μ²­ 전솑 κΈ°λŠ₯을 μ œκ³΅ν•˜λŠ” ν΄λΌμ΄μ–ΈνŠΈ μ‚¬μ΄λ“œ Web API

  • XTMLHttpRequest 객체보닀 μ‚¬μš©λ²•μ΄ κ°„λ‹¨ν•˜λ©°, ν”„λ‘œλ―ΈμŠ€λ₯Ό μ§€μ›ν•˜κΈ° λ•Œλ¬Έμ— 비동기 처리λ₯Ό μœ„ν•œ 콜백 νŒ¨ν„΄μ—μ„œ μžμœ λ‘­λ‹€.
const promise = fetch(url [, options]);

fetch ν•¨μˆ˜λŠ” HTTP 응닡을 λ‚˜νƒ€λ‚΄λŠ” Response 객체λ₯Ό λž˜ν•‘ν•œ Promise 객체λ₯Ό λ°˜ν™˜

fetch("https://jsonplaceholder.typicode.com/todos/1").then((res) => console.log(res));

// Response
// {
//   type: 'cors',
//   url: 'https://jsonplaceholder.typicode.com/todos/1',
//   redirected: false,
//   status: 200,
//   ok: true,
//   …
// }
  • Response 객체의 Response.prototype μ—λŠ” Response 객체에 ν¬ν•¨λ˜μ–΄ μžˆλŠ” HTTP 응닡 λͺΈμ²΄λ₯Ό μœ„ν•œ λ‹€μ–‘ν•œ λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•œλ‹€.
    • Response.prototpye.json λ©”μ„œλ“œ : fetch ν•¨μˆ˜κ°€ λ°˜ν™˜ν•œ ν”„λ‘œλ―ΈμŠ€κ°€ λž˜ν•‘ν•˜κ³  μžˆλŠ” MIME νƒ€μž…μ΄ application/json 인 HTTP 응닡 λͺΈμ²΄λ₯Ό 취득
      • Response κ°μ²΄μ—μ„œ HTTP Response Bodyλ₯Ό μ·¨λ“ν•˜μ—¬ 역직렬화(deserializing)ν•œλ‹€. ( μ„œλ²„ 응닡 데이터 νšλ“ )

fetch - CRUD μ˜ˆμ‹œ

// HTTP Request(μš”μ²­) λͺ¨μŒ 객체 생성
const request = {
  get(url) {
    return fetch(url);
  },

  post(url, payload) {
    return fetch(url, {
      method: "POST",
      headers: { "content-Type": "application/json " },
      body: JSON.stringify(payload),
    });
  },

  patch(url, payload) {
    return fetch(url, {
      method: "PATCH",
      headers: { "content-Type": "application/json " },
      body: JSON.stringify(payload),
    });
  },

  delete(url) {
    return fetch(url, { method: "DELETE " });
  },
};

const url = "HTTP 톡신 μš”μ²­ν•  url";

// GET μ˜ˆμ‹œ
request
  .get(url)
  .then((res) => res.json())
  .then((data) => console.log(data));

// POST μ˜ˆμ‹œ
request
  .post(url, { obj })
  .then((res) => res.json())
  .then((data) => console.log(data))
  .catch((err) => console.error(err));

// PATCH μ˜ˆμ‹œ
request
  .patch(url, { obj })
  .then((res) => res.json())
  .then((data) => console.log(data))
  .catch((err) => console.error(err));

// DELETE μ˜ˆμ‹œ
request
  .delete(url, { obj })
  .then((res) => res.json())
  .then((data) => console.log(data))
  .catch((err) => console.error(err));