[week_05] ๋น„๋™๊ธฐ ํ†ต์‹ ๊ณผ Promise
elice/WIL

[week_05] ๋น„๋™๊ธฐ ํ†ต์‹ ๊ณผ Promise

1. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ œ์–ด ํ๋ฆ„

  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๋‹ค๋ฅธ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์™€ ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ๋น„๋™๊ธฐ ๋™์ž‘์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.
    • ํŠน์ • ์ฝ”๋“œ์˜ ์—ฐ์‚ฐ์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ์ฝ”๋“œ์˜ ์‹คํ–‰์„ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ  ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ๋จผ์ € ์‹คํ–‰
    • console.log('1');
      
      setTimeout(() => {console.log('2');}, 1000);
      
      console.log('3');
    • ๋‹ค๋ฅธ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ์–ธ์–ด์—์„œ์˜ ๊ฒฐ๊ณผ: '1'์ถœ๋ ฅ →  1์ดˆ ํ›„ '2' ์ถœ๋ ฅ → '3'์ถœ๋ ฅ
    • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ์˜ ๊ฒฐ๊ณผ: '1'์ถœ๋ ฅ → '3'์ถœ๋ ฅ → 1์ดˆ ํ›„ '2' ์ถœ๋ ฅ
  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„
    • ํ•˜๋‚˜์˜ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋กœ ๊ตฌ์„ฑ (์ฆ‰, ๋‹จ ํ•˜๋‚˜์˜ Call stack์„ ์‚ฌ์šฉ)
    • ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ์ฝ”๋“œ๋ฅผ ์ฝ์–ด ํ•œ ์ค„์”ฉ ์‹คํ–‰
    • ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ์—์„œ๋Š” ์œ ์ € ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ํ™”๋ฉด์„ ๊ทธ๋ฆฐ๋‹ค

1-1. ๋™๊ธฐ์  ์ œ์–ด ํ๋ฆ„

  • ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์ฝ”๋“œ๊ฐ€ ์ข…๋ฃŒ๋˜๊ธฐ ์ „๊นŒ์ง€ ๋‹ค์Œ ์ค„์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ
  • ๋ถ„๊ธฐ๋ฌธ, ๋ฐ˜๋ณต๋ฌธ, ํ•จ์ˆ˜ ํ˜ธ์ถœ ๋“ฑ
  • ์ฝ”๋“œ์˜ ํ๋ฆ„๊ณผ ์‹ค์ œ ์ œ์–ด ํ๋ฆ„์ด ๋™์ผ
  • ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ ๊ธด ์‹œ๊ฐ„ ์ ์œ ํ•˜๋ฉด, ํ”„๋กœ๊ทธ๋žจ์„ ๋ฉˆ์ถ”๊ฒŒ ํ•œ๋‹ค.

1-2. ๋น„๋™๊ธฐ์  ์ œ์–ด ํ๋ฆ„

  • ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์ฝ”๋“œ๊ฐ€ ์ข…๋ฃŒ๋˜๊ธฐ ์ „์— ๋‹ค์Œ ๋ผ์ธ์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ
  • ํ”„๋กœ๋ฏธ์Šค, ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜ ๋“ฑ
  • ์ฝ”๋“œ ํ๋ฆ„๊ณผ ์‹ค์ œ ์ œ์–ด ํ๋ฆ„์ด ๋‹ค๋ฅด๋‹ค
  • ๋น„๋™๊ธฐ ์ž‘์—…์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ๋‹ค๋ฅธ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.

์ถœ์ฒ˜: https://adrianmejia.com/asynchronous-vs-synchronous-handling-concurrency-in-javascript/

 

2. ์ด๋ฒคํŠธ ๋ฃจํ”„

  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„์€ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ๋Œ€์‹ , ๋น„๋™๊ธฐ ์ฝ”๋“œ๋Š” ์ •ํ•ด์ง„ ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•˜์—ฌ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๋•Œ, ์ด ํ•จ์ˆ˜๋“ค์„ API๋ผ ํ•œ๋‹ค.
    • setTimeout, XMLHttpRequest, fetch ๋“ฑ์˜ Web API๊ฐ€ ์žˆ๋‹ค.
  • node.js์˜ ๊ฒฝ์šฐ ํŒŒ์ผ ์ฒ˜๋ฆฌ API, ์•”ํ˜ธํ™” API ๋“ฑ์„ ์ œ๊ณตํ•œ๋‹ค.

2-1. ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๋ชจ๋ธ

  • ๋ฉ”์ธ ์Šค๋ ˆ๋“œ: Call stack์„ ์ด์šฉํ•ด์„œ ์ฝ”๋“œ๋ฅผ ์ฝ๊ณ  ์‹คํ–‰
  • ๋น„๋™๊ธฐ ํ™˜๊ฒฝ: Task queue, Job queue
  • ์ด๋ฒคํŠธ ๋ฃจํ”„: ๋น„๋™๊ธฐ ์ฝ”๋“œ ํ˜ธ์ถœ, ๋น„๋™๊ธฐ ์ฝ”๋“œ ์ฒ˜๋ฆฌ, ๊ฒฐ๊ณผ ํ•จ์ˆ˜ ๋ฐ˜ํ™˜
  • ๋น„๋™๊ธฐ ์ฝ”๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ชจ๋“ˆ์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„ ์™ธ๋ถ€์— ์กด์žฌ
  • API ๋ชจ๋“ˆ์€ ๋น„๋™๊ธฐ ์š”์ฒญ์„ ์ฒ˜๋ฆฌ ํ›„ Task queue์— ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ๋„ฃ๋Š”๋‹ค.
  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„์€ Call stack์ด ๋น„์›Œ์ง€๋ฉด, Task queue์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

2-2. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ์˜ˆ์‹œ

  • Web API: ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ œ๊ณต๋˜๋Š” API์ด๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„์—์„œ ์ •์˜๋˜์ง€ ์•Š์•˜๋˜ setTimeout์ด๋‚˜ HTTP ์š”์ฒญ(ajax) ๋ฉ”์„œ๋“œ, DOM ์ด๋ฒคํŠธ ๋“ฑ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์›ํ•œ๋‹ค.
  • Task Queue: ์ด๋ฒคํŠธ ๋ฐœ์ƒ ํ›„ ํ˜ธ์ถœ๋˜์–ด์•ผ ํ•  ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋“ค์ด ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ณต๊ฐ„. ์ด๋ฒคํŠธ ๋ฃจํ”„๊ฐ€ ์ •ํ•œ ์ˆœ์„œ๋Œ€๋กœ ์ค„์„ ์„œ ์žˆ์œผ๋ฏ€๋กœ ์ฝœ๋ฐฑ ํ(Callback Queue)๋ผ๊ณ ๋„ ํ•œ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ๋น„๋™๊ธฐ๋กœ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜๋“ค์€ ํ˜ธ์ถœ ์Šคํƒ(Call Stack)์— ์Œ“์ด์ง€ ์•Š๊ณ  ํƒœ์Šคํฌ ํ(Task Queue)๋กœ ๋ณด๋‚ด์ง„๋‹ค.
  • Event Loop: ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ ํ˜ธ์ถœํ•  ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋“ค์„ ๊ด€๋ฆฌํ•˜๊ณ , ํ˜ธ์ถœ๋œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์–ด๋–ค ์ผ์ด ๋ฐœ์ƒํ•˜๋Š”์ง€ ์•Œ์•„๋ณด์ž.

console.log('Hi');

setTimeout(function cb1() { 
    console.log('cb1');
}, 5000);

console.log('Bye');

 

1. ์ œ์ผ ์ฒ˜์Œ์—๋Š” ์ฝ˜์†”, Call Stack ๋“ฑ ๋ชจ๋“  ๊ฒƒ์ด ๋น„์›Œ์ง„ ์ƒํƒœ๋กœ ์‹œ์ž‘๋œ๋‹ค.

2. console.log('Hi')๊ฐ€ ํ˜ธ์ถœ๋˜์–ด Call Stack์— ์Œ“์ธ๋‹ค.

3. console.log('Hi')๊ฐ€ ์‹คํ–‰๋˜์–ด ์ฝ˜์†”์— Hi๊ฐ€ ์ฐํžŒ๋‹ค.

4. console.log('Hi')๊ฐ€ ๋ฆฌํ„ด๋˜๋ฉฐ Call Stack์—์„œ ์ œ๊ฑฐ๋œ๋‹ค.

5. setTimeout(function cb1() {...})๊ฐ€ ์‹คํ–‰๋˜๋ฉด์„œ Call Stack์— setTimeoutํ•จ์ˆ˜๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค.

6. setTimeoutํ•จ์ˆ˜๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„์ด ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ณ  Web API๊ฐ€ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ Callbackํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•œ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๋Š” Wep API์˜ ์ผ๋ถ€๋กœ ํƒ€์ด๋จธ๋ฅผ ๋งŒ๋“ค๊ณ , Call Stack์œผ๋กœ๋ถ€ํ„ฐ ์š”์ฒญ๋ฐ›์€ setTimeout์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ์ค€๋น„๋ฅผ ํ•œ๋‹ค.

7. Call Stack์—์„œ๋Š” setTimeout์ž‘์—…์ด ์ œ๊ฑฐ๋œ๋‹ค.

8. console.log('Bye')๊ฐ€ ํ˜ธ์ถœ๋˜์–ด Call Stack์— ์Œ“์ธ๋‹ค.

9. console.log('Bye')๊ฐ€ ์‹คํ–‰๋˜์–ด ์ฝ˜์†”์— Bye๊ฐ€ ์ฐํžŒ๋‹ค.

10. console.log('Bye')๊ฐ€ ๋ฆฌํ„ด๋˜๋ฉฐ Call Stack์—์„œ ์ œ๊ฑฐ๋œ๋‹ค.

11. Web API๊ฐ€ setTimeout์ž‘์—…์„ ์‹คํ–‰ํ•œ๋‹ค. 5์ดˆ ํ›„ ํƒ€์ด๋จธ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด cb1 Callbackํ•จ์ˆ˜๋ฅผ Task(Callback) Queue๋กœ ๋ณด๋‚ธ๋‹ค.

12. Event Loop๋Š” Call Stack์ด ๋น„์–ด์žˆ์œผ๋ฉด Task(Callback) Queue์—์„œ ํ•จ์ˆ˜๋ฅผ ํ•˜๋‚˜์”ฉ ๊บผ๋‚ด Call Stack์— ๋„ฃ๋Š”๋‹ค.

13. cb1์ด ์‹คํ–‰๋˜๋ฉด์„œ console.log('cb1')์ด ํ˜ธ์ถœ๋˜์–ด Call Stack์— ์Œ“์ธ๋‹ค.

14. console.log('cb1')์ด ์‹คํ–‰๋˜์–ด ์ฝ˜์†”์— cb1์ด ์ฐํžŒ๋‹ค.

15. console.log('cb1')์ด ๋ฆฌํ„ด๋˜๋ฉฐ Call Stack์—์„œ ์ œ๊ฑฐ๋œ๋‹ค.

16. cb1์ด Call Stack์—์„œ ์ œ๊ฑฐ๋˜๋ฉฐ ๋ชจ๋“  ์ž‘์—…์ด ์™„๋ฃŒ๋œ๋‹ค.

 

3. Promise

  • ๋น„๋™๊ธฐ API ์ค‘ ํ•˜๋‚˜์ด๋‹ค.
  • Job queue(microtask queue)๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  • Job queue๋Š” Task queue๋ณด๋‹ค ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’๋‹ค.
  • setTimeout(() => {console.log("ํƒ€์ž„์•„์›ƒ1");}, 0);
    
    Promise.resolve().then(() => console.log("ํ”„๋กœ๋ฏธ์Šค1"));
    
    setTimeout(() => {console.log("ํƒ€์ž„์•„์›ƒ2");}, 0);
    
    Promise.resolve().then(() => console.log("ํ”„๋กœ๋ฏธ์Šค2"));
    
    // ํ”„๋กœ๋ฏธ์Šค1 ํ”„๋กœ๋ฏธ์Šค2
    // ํƒ€์ž„์•„์›ƒ1 ํƒ€์ž„์•„์›ƒ2
  • ๋น„๋™๊ธฐ ์ž‘์—…์„ ํ‘œํ˜„ํ•˜๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด
  • ๋น„๋™๊ธฐ ์ž‘์—…์˜ ์ง„ํ–‰(Pending), ์„ฑ๊ณต(Fulfilled, Resolved โžก .then()), ์‹คํŒจ(Rejected โžก .catch()) ์ƒํƒœ๋ฅผ ํ‘œํ˜„
  • ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ์˜ ์ˆœ์„œ๋ฅผ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

3-1. Promise ์ƒ์„ฑ์ž

  • new Promise(callback)
  • callback ํ•จ์ˆ˜๋Š” (resolve, reject) ๋‘ ์ธ์ž๋ฅผ ๋ฐ›๋Š”๋‹ค.
  • Promise ์„ฑ๊ณต ์‹œ: resolve ํ˜ธ์ถœ
  • Promise ์‹คํŒจ ์‹œ: reject ํ˜ธ์ถœ
  • let promise = new Promise((resolve, reject) => {
    	if (Math.random() < 0.5) { return reject("์‹คํŒจ"); }
        resolve(10);
    });
  • .then(): ์„ฑ๊ณตํ–ˆ์„ ๋•Œ ์‹คํ–‰ํ•  ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋„˜๊น€
    • .then(callback1, callback2)๋กœ callback1์˜ ์ž๋ฆฌ์— ์„ฑ๊ณต, callback2์˜ ์ž๋ฆฌ์— ์‹คํŒจ ๋ฉ”์„œ๋“œ๋ฅผ ์ธ์ž๋กœ ๋„˜๊ธธ ์ˆ˜ ์žˆ๋‹ค.
    • ex) .then(function onSuccess() {...}, function onFail() {...})
  • .catch(): ์‹คํŒจํ–ˆ์„ ๋•Œ ์‹คํ–‰ํ•  ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋„˜๊น€
  • .finally(): ์„ฑ๊ณต/์‹คํŒจ ์—ฌ๋ถ€์™€ ์ƒ๊ด€์—†์ด ๋ชจ๋‘ ์‹คํ–‰ํ•  ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋„˜๊น€
  • .all(): Promise์˜ ๋ฐฐ์—ด์„ ๋ฐ›์•„ ๋ชจ๋‘ ์„ฑ๊ณต ์‹œ ๊ฐ Promise์˜ resolved๊ฐ’์„ ๋ฐฐ์—ด๋กœ ๋ฐ˜ํ™˜
    • ํ•˜๋‚˜์˜ Promise๋ผ๋„ ์‹คํŒจํ•  ์‹œ, ๊ฐ€์žฅ ๋จผ์ € ์‹คํŒจํ•œ Promise์˜ ์‹คํŒจ ์ด์œ ๋ฅผ ๋ฐ˜ํ™˜
    • Promise.all([promise1, promise2, promise3])
      	.then(values => {console.log("๋ชจ๋‘ ์„ฑ๊ณต: ", values)})
          .catch(e => {console.log("ํ•˜๋‚˜๋ผ๋„ ์‹คํŒจ: ", e)})

3-2. Promise ๋ฉ”์„œ๋“œ ์ฒด์ธ

  • then/catch ๋ฉ”์„œ๋“œ๊ฐ€ ๋˜ ๋‹ค๋ฅธ promise๋ฅผ ๋ฆฌํ„ดํ•˜์—ฌ, ๋น„๋™๊ธฐ ์ฝ”๋“œ์— ์ˆœ์„œ๋ฅผ ๋ถ€์—ฌ
  • ์ด๋ ‡๊ฒŒ ๋™์ผํ•œ ๊ฐ์ฒด์— ๋ฉ”์„œ๋“œ๋ฅผ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ ์ฒด์ด๋‹(Chaining)์ด๋ผ ํ•œ๋‹ค.
  • ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ์ฃผ์ฒด๊ฐ€ ํ•จ์ˆ˜๋ฅผ ๋๋‚ธ ๋’ค ์ž๊ธฐ ์ž์‹ ์„ ๋ฆฌํ„ดํ•˜๋„๋ก ํ•˜์—ฌ ๊ตฌํ˜„ํ•œ๋‹ค.

 

 

 

์ฐธ๊ณ 

https://blog.sessionstack.com/how-javascript-works-event-loop-and-the-rise-of-async-programming-5-ways-to-better-coding-with-2f077c4438b5

 

https://ingg.dev/js-work/