Promise

: Promise is a JavaScript object for asynchronous operation.
๋น„๋™๊ธฐ๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” Object

๋„คํŠธ์›Œํฌ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๊ฑฐ๋‚˜, ํŒŒ์ผ์—์„œ ํฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜ค๋Š” ๊ณผ์ •์€ ์‹œ๊ฐ„์ด ๊ฝค ๊ฑธ๋ฆฐ๋‹ค
์ด๋Ÿฐ ์ผ์„ ๋™๊ธฐ์ , synchronous๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉด ํŒŒ์ผ์„ ์ฝ์–ด์˜ค๊ณ , ๋„คํŠธ์›Œํฌ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š” ๋™์•ˆ ๋‹ค์Œ ๋™์ž‘์„ ์ˆ˜ํ–‰ ๋ชปํ•จ.

๋น„๋™๊ธฐ์ ์ธ ๊ฒƒ์„ ์ˆ˜ํ–‰ํ•  ๋•Œ, callback ํ•จ์ˆ˜ ๋Œ€์‹ ์— ์œ ์šฉํ•˜๊ฒŒ ์“ธ ์ˆ˜ ์žˆ๋‹ค!
์ •ํ•ด์ง„ ์žฅ์‹œ๊ฐ„์˜ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๋‚˜์„œ, ์ •์ƒ์ ์ธ ๊ธฐ๋Šฅ์ด ์ˆ˜ํ–‰๋๋‹ค๋ฉด ์„ฑ๊ณต ๋ฉ”์‹œ์ง€์™€ ์ฒ˜๋ฆฌ๋œ ๊ฒฐ๊ณผ๊ฐ’์„ ์ „๋‹ฌ.
๊ธฐ๋Šฅ ์ˆ˜ํ–‰์ค‘์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํžˆ๋ฉด ์—๋Ÿฌ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

  • ์˜ˆ์‹œ ์•„์ง ์˜คํ”ˆํ•˜์ง€ ์•Š์€ ๊ฐ•์˜, ์ด๋ฉ”์ผ์„ ์ž…๋ ฅํ•ด์„œ ๋ฏธ๋ฆฌ ๋“ฑ๋กํ•˜๋ฉด ๊ฐ•์˜๊ฐ€ ์˜คํ”ˆ๋์„ ๋•Œ ๋ฉ”์ผ์„ ๋ณด๋‚ด์คŒ.

A์˜ ๊ฒฝ์šฐ

  • A๊ฐ€ (์ƒ์„ฑ๋œ)promise์— ๋“ฑ๋ก
  • ์‹œ๊ฐ„์ด ์ง€๋‚œ ํ›„ ์ฝ”์Šค๊ฐ€ ์˜คํ”ˆ๋˜๋ฉด ๋ฉ”์ผ์„ ๋ฐ›์Œ(promise๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ๊ฐ’์„ ์ „๋‹ฌํ•จ)

B์˜ ๊ฒฝ์šฐ

  • B๊ฐ€ (์ด๋ฏธ ์„ฑ๊ณต์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋œ)promise์— ๋“ฑ๋ก
  • ์ด๋ฏธ ์˜คํ”ˆ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ”๋กœ ๋ฉ”์ผ์„ ๋ฐ›์Œ


โญ๏ธ ๊ณต๋ถ€ ํฌ์ธํŠธ!

  1. state
    • Promise๊ฐ€ ๋ฌด๊ฑฐ์šด operation์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์žˆ๋Š” ์ค‘์ธ์ง€
    • ์•„๋‹ˆ๋ฉด ๊ธฐ๋Šฅ์ˆ˜ํ–‰์ด ๋๋‚˜์„œ ์„ฑ๊ณต/์‹คํŒจํ–ˆ๋Š”์ง€
  2. producer / consumer์˜ ์ฐจ์ด์ 
    • ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” producer๋ฅผ producing, ์ œ๊ณตํ•˜๋Š” ์‚ฌ๋žŒ๊ณผ
    • ์ œ๊ณต๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์“ฐ๋Š” ์‚ฌ๋žŒ, ํ•„์š”๋กœ ํ•˜๋Š” ์‚ฌ๋žŒ consumer

1-1. state

โญ๏ธ pending => fulfilled or rejected โญ๏ธ
(์ฒ˜๋ฆฌ์ค‘ => ์„ฑ๊ณต or ์‹คํŒจ)

  • pending
    promise๊ฐ€ ๋งŒ๋“ค์–ด์ ธ์„œ ์šฐ๋ฆฌ๊ฐ€ ์ง€์ •ํ•œ operation์ด ์‚ฌ์šฉ์ค‘์ผ ๋•Œ
  • fulfilled
    operation์„ ์„ฑ๊ณต์ ์œผ๋กœ ๋‹ค ๋๋ƒˆ์„ ๋•Œ
  • rejected
    ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†๊ฑฐ๋‚˜, ๋„คํŠธ์›Œํฌ์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ์„ ๋•Œ

1-2. producer vs consumer

  • producer
    ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•ด์„œ ํ•ด๋‹นํ•˜๋Š” data๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๋Š”, promise์˜ object
  • consumer
    ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” data๋ฅผ ์†Œ๋น„ํ•˜๋Š”, promise์˜ object

1. producer

: when new promise is created, the excutor runs automatically.

  • promise๋ฅผ ๋งŒ๋“œ๋Š” ์ˆœ๊ฐ„,์ „๋‹ฌํ•œ ์ฝœ๋ฐฑํ•จ์ˆ˜ excutor๊ฐ€ ๋ฐ”๋กœ ์‹คํ–‰๋œ๋‹ค!
const promise = new Promise((resolve, reject) => {
  // doing some heavy work(network, read files)
  console.log("doing something...");
  setTimeout(() => {
    resolve("ellie");
    reject(new Error("no network"));
    // ์„ฑ๊ณต์ ์œผ๋กœ ๋„คํŠธ์›Œํฌ์—์„œ ๋ฐ›์•„์˜จ, ํŒŒ์ผ์—์„œ ์ฝ์–ด์˜จ, ์ด๊ฒƒ๋“ค์„ ๊ฐ€๊ณตํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝœ๋ฐฑํ•จ์ˆ˜ resolve๋ฅผ ํ†ตํ•ด์„œ ์ „๋‹ฌ
  }, 2000);
});
// executor๋ผ๋Š” ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•ด์•ผํ•จ. ๊ทธ ์ฝœ๋ฐฑํ•จ์ˆ˜๋Š” ๋˜ ๋‹ค๋ฅธ ๋‘๊ฐ€์ง€์˜ ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ๋ฐ›๋Š”๋‹ค
  • => ๋งŒ์•ฝ ์ด promise์•ˆ์— ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ๋‹ค๋ฉด, promise๊ฐ€ ๋งŒ๋“ค์–ด์ง€๋Š” ์ˆœ๊ฐ„! ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค

โญ๏ธ ์‚ฌ์šฉ์ž๊ฐ€ ์š”๊ตฌํ–ˆ์„ ๋•Œ์—๋งŒ(๋ฒ„ํŠผํด๋ฆญ) ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฑธ ๊ผญ ๊ธฐ์–ตํ•  ๊ฒƒ!

2. consumers : then, catch, finally

  • then : ๊ฐ’์ด ์ •์ƒ์ ์œผ๋กœ ์ˆ˜ํ–‰์ด ๋œ๋‹ค๋ฉด!
  • catch : ๊ฐ’์ด ์ •์ƒ์ ์œผ๋กœ ์ˆ˜ํ–‰์ด ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด!
  • finally : ์„ฑ๊ณตํ•˜๋“  ์‹คํŒจํ•˜๋“  ์ƒ๊ด€์—†์ด ๋ฌด์กฐ๊ฑด ๋งˆ์ง€๋ง‰์— ํ˜ธ์ถœ
promise
  .then((value) => {
    console.log(value);
  })
  .catch((error) => {
    console.log(error);
  })
  .finally(() => console.log("finally"));

.then์€ ๋˜‘๊ฐ™์€ promise๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ์—ฐ๊ฒฐํ•ด์„œ ์“ธ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ.
(array-api์—์„œ ๋˜‘๊ฐ™์€ ๋ฐฐ์—ด์„ ๋ฆฌํ„ดํ•˜๋Š” map์œผ๋กœ ์ฒด์ด๋‹ํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ)

์„ฑ๊ณตํ–ˆ๋‹ค๋ฉด ๊ทธ ๊ฐ’์„ ๋ฐ›์•„์™€์„œ, ์›ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•ด์ฃผ๋Š” ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑ


3. Promise chaining promise ์—ฐ๊ฒฐํ•˜๊ธฐ

const fetchNumber = new Promise((resolve, reject) => {
  setTimeout(() => resolve(1), 1000);
});
fetchNumber
  .then((num) => num * 2)
  .then((num) => num * 3)
  .then((num) => { //๋‹ค๋ฅธ ์„œ๋ฒ„์— ๋ณด๋‚ด์„œ ๋‹ค๋ฅธ ์ˆซ์ž๋กœ ๋ณ€ํ™˜๋œ ๊ฐ’์„ ๊ฐ€์ ธ์˜จ๋‹ค
    return new Promise((resolve, reject) => {
      setTimeout(() => resolve(num - 1), 1000);
    });
  })
  .then((num) => console.log(num));


4. Error Handling

const getHen = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve("๐Ÿ“"), 1000);
  });
const getEgg = (hen) =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve(`${hen} => ๐Ÿฅš`), 1000);
    setTimeout(() => reject(new Error(`error! ${hen} => ๐Ÿฅš`)), 1000);
  });
const cook = (egg) =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve(`${egg} => ๐Ÿณ`), 1000);
  });
getHen()
  .then((hen) => getEgg(hen))
  .then((egg) => cook(egg))
  .then((meal) => console.log(meal));


์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ๋•Œ, ๋ฐ›์•„์˜ค๋Š” value๋ฅผ ๋‹ค๋ฅธํ•จ์ˆ˜๋กœ ๋ฐ”๋กœ ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š”, ์•„๋ž˜์ฒ˜๋Ÿผ ์ถ•์•ฝํ•  ์ˆ˜ ์žˆ๋‹ค

getHen() //
  .then(getEgg)
  .then(cook)
  .then(console.log);
  • then์—์„œ ๋ฐ›์•„์˜ค๋Š” value๋ฅผ ๋ฐ”๋กœ getEggํ•จ์ˆ˜์— ๋„ฃ์–ด์„œ ํ˜ธ์ถœ
  • then์—์„œ ๋ฐ›์•„์˜ค๋Š” value๋ฅผ ๋ฐ”๋กœ cookํ•จ์ˆ˜์— ๋„ฃ์–ด์„œ ํ˜ธ์ถœ
getHen() //
  .then(getEgg)
  .catch((error) => {
    return "๐Ÿ–";
  })
  .then(cook)
  .then(console.log)
  .catch(console.log);

์ค‘๊ฐ„์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒจ๋„ ์ „์ฒด์ ์ธ promise ์ฒด์ธ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ๋นต!๊พธ! ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค€๋‹ค
ํŠน์ •๋ถ€๋ถ„์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์„ ๋•,
๋ฐ”๋กœ ๊ทธ ๋‹ค์Œ์— catch๋ฅผ ์ž‘์„ฑํ•ด์„œ ๋ฐ”๋กœ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ.๐Ÿ”ฅ

5. ๐Ÿ’ฉ => โœจโœจโœจโœจ (์ฝœ๋ฐฑ์ง€์˜ฅ ๊ฐœ์„ ํ•˜๊ธฐ)

(before) callback์œผ๋กœ๋งŒ ์ž‘์„ฑํ•œ ์ฝ”๋“œ ๐Ÿ’ฉ

class UserStorage {
  loginUser(id, password, onSuccess, onError) {
    setTimeout(() => {
      if (
        (id === "ellie" && password === "dream") ||
        (id === "coder" && password === "academy")
      ) {
onSuccess(id);
      } else {
        onError(new Error("not found"));
      }
    }, 2000);
  }

  getRoles(user, onSuccess, onError) {
    setTimeout(() => {
      if (user === "ellie") {
        onSuccess({ name: "ellie", role: "admin" });
      } else {
        onError(new Error("not 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} role`
        );
      },

      (error) => {
        onsole.log("error");
      }
    );
  },
  (error) => console.log("error")
);

(after) promise๋กœ ๋ณ€๊ฒฝํ•œ ์ฝ”๋“œ! โœจ

class UserStorage {
  loginUser(id, password) {
    return new Promise((resolve, reject) => {
      // promise์•ˆ์—์„œ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋„ฃ๊ณ 
      setTimeout(() => {
        if (
          (id === "ellie" && password === "dream") ||
          (id === "coder" && password === "academy")
        ) {
          resolve(id);
        } else {
          reject(new Error("not found"));
        }
      }, 2000);
    });
  }

  getRoles(user) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (user === "ellie") {
          resolve({ name: "ellie", role: "admin" });
        } else {
          reject(new Error("not access"));
        }
      }, 1000);
    });
  }
}

const userStorage = new UserStorage();
const id = prompt("enter your id");
const password = prompt("enter your password");
userStorage
  .loginUser(id, password) //loginUser๋ฅผ ํ˜ธ์ถœํ—ค์„œ id์™€ psw๋ฅผ ์•Œ๋ ค์คŒ
  .then((user) => userStorage.getRoles(user)) // ์•Œ๋ ค์ฃผ๋Š”๋ฐ ์„ฑ๊ณตํ•˜๋ฉด
  .then((user) => alert(`Hello, ${user.name}, you have a ${user.role} role`)) //์ถœ๋ ฅ
  .catch(console.log);