ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

์„น์…˜ 9:Asynchronous JavaScript

132. Section Overview

//1
setTimeout(()=>{console.log('1', 'is the loneliest number')}, 0)
setTimeout(()=>{console.log('2', 'can be as bad as one')}, 10)

//2
Promise.resolve('hi').then((data)=> console.log('2', data))

//3
console.log('3','is a crowd')

// --output--
// 3 is a crowd
// 2 hi            // Promise
// -- program return undefined --
// 1 is the loneliest number    // setTimeout 0
// 2 can be as bad as one        // setTimeout 10

Web API - DOM ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ, fetch(), setTimeout() ...

โ€‹

135. Promises

A promis is an object that may produce a single value some time in the future.

either a resolved value, or a reason that it’s not resolved (rejected)

states : pending / fulfilled / rejected

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise

callback pyramid of dom

movePlayer(100, 'Left', function () {
  movePlayer(400, 'Left', function () {
    movePlayer(10, 'Right', function () {
      movePlayer(330, 'Left', function () {
      });
    });
  });
});

์ฝœ๋ฐฑ์˜ ์ค‘์ฒฉ ์˜ˆ์‹œ

really really complicated code!

โ€‹

promise exercise

const promise = new Promise((resolve, reject) => {
  if (true) {
    resolve('Stuff Worked');
  } else {
    reject('Error, it broke');
  }
});

promise
  .then(result => result + '!')
  .then(result2 => {
    console.log(result2);
  })

์ถœ๋ ฅ๊ฒฐ๊ณผ

Stuff Worked!
Promise {<fulfilled>: undefined}

then์˜ ์ฒซ๋ฒˆ์งธ ์ธ์ž๋Š” ์•ž๋‹จ promise ์˜ resolve ๋œ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ด๊น€

โ€‹

promise
  .then(result => {
    //throw "์—๋Ÿฌ1"
    return result + '!'
  })
  .then(result2 => {
    throw "์—๋Ÿฌ2"
    console.log(result2);
  })
  .catch((err) => console.log(err))

.catch๊ฐ€ ์—๋Ÿฌ2 ์žก์•„์„œ ์ถœ๋ ฅํ•จ

catch๊ตฌ๋ฌธ ์ด์ „์— ์žˆ๋Š” ์—๋Ÿฌ๋งŒ ์žก์„์ˆ˜์žˆ์Œ

โ€‹

const promise = new Promise((resolve, reject) => {
  if (false) {
    resolve('Stuff Worked');
  } else {
    reject('Error, it broke');
  }
});


// Uncaught (in promise) Error, it broke

.then ์•ˆํ•ด๋„ reject๋œ๊ฐ’ ๋ฐ”๋กœ๋‚˜์˜ค๋„น..?

โ€‹

promise all

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'ONE');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 2000, 'TWO');
});

const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, 'THREE');
});

Promise.all([promise1, promise2, promise3])
  .then(values => {
    console.log(values);
  })

(3000ms ํ›„) until promises were resolved

(3) ["ONE", "TWO", "THREE"]

โ€‹

promise๋จผ์ € ์„ ์–ธํ•œ๋‹ด์—
3์ดˆ ๋˜๊ธฐ ์ „์— promise.all.thenํ•˜๋ฉด pending ์ƒํƒœ๋กœ ๋‚˜์˜ด

3์ดˆ ์ง€๋‚˜๊ณ  ๋‚˜์„œ all ํ•˜๋ฉด ๋ฐ”๋กœ ์ถœ๋ ฅ

 

โ€‹

const urls = [
  'https://jsonplaceholder.typicode.com/users',
  'https://jsonplaceholder.typicode.com/posts',
  'https://jsonplaceholder.typicode.com/albums',
  'o_o?'
];

Promise.all(urls.map(url => {
  return fetch(url).then(resp => resp.json());
}))
  .then(results => {
    console.log(results[0]);
    console.log(results[1]);
    console.log(results[2]);
  })
  .catch(() => console.log("ERROR!"));

// --output--
// Promise {<pending>}
// GET chrome-extension://laookkfknpbbblfpciffpaejjkokdgca/o_o? net::ERR_FILE_NOT_FOUND
// ERROR!

์ด์ƒํ•œ url ์ด ์žˆ์–ด์„œ fetch ์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋‚˜๋ฉด catch๋กœ ์žก์„ ์ˆ˜ ์žˆ์Œ

โ€‹

fetch('https://jsonplaceholder.typicode.com/users') // return Promise

โ€‹

โ€‹

136. ES8 - Async Await

async function : a function that returns a promise.

benefit of async/await is that it makes code easier to read.

 

  • 1๋‹จ๊ณ„ : callback pyramid of dom
movePlayer(100, 'Left', function () {
  movePlayer(400, 'Left', function () {
    movePlayer(10, 'Right', function () {
      movePlayer(330, 'Left', function () {
      });
    });
  });
});

 

  • 2๋‹จ๊ณ„ : Promise
movePlayer(100, 'Left')
  .then(() => movePlayer(400, 'Left'))
  .then(() => movePlayer(10, 'Right'))
  .then(() => movePlayer(330, 'Left'))

 

  • 3๋‹จ๊ณ„ : Async/Await
async function playerStart() {
  const firstMove = await movePlayer(100, 'Left'); // pause
  await movePlayer(400, 'Left'); // pause
  await movePlayer(10, 'Right'); // pause
  await movePlayer(330, 'Left'); // pause
}

โ€‹

 

more realistic example (use fetch function)

// Promise
fetch('https://jsonplaceholder.typicode.com/users')
  .then(resp => resp.json())
  .then(console.log)

// Async/Await
async function fetchUsers() {
  const response = await fetch('https://jsonplaceholder.typicode.com/users');
  const data = await response.json();
  console.log(data);
}

++ response.json()์— ๋Œ€ํ•ด

response.json() does return a promise as well (as it waits for the body to load)

Why does .json() return a promise? : https://stackoverflow.com/questions/37555031/why-does-json-return-a-promise

โ€‹

 

more realistic example 2 (Promise.all)

const urls = [
  'https://jsonplaceholder.typicode.com/users',
  'https://jsonplaceholder.typicode.com/posts',
  'https://jsonplaceholder.typicode.com/albums',
];

// Promise.all
Promise.all(urls.map(url =>
  fetch(url).then(resp => resp.json())
))
  .then(results => {
    console.log("Users  : ", results[0]);
    console.log("Posts  : ", results[1]);
    console.log("Albums  : ", results[2]);
  })
  .catch("Oooops!");

// Async/Await
const getData = async function () {
  const [users, posts, albums] =
    await Promise.all(urls.map(url =>
      fetch(url).then(resp => resp.json())
    ));

  console.log("Users  : ", users);
  console.log("Posts  : ", posts);
  console.log("Albums  : ", albums);
}

 

error handing

Promise.all ์—์„œ ์ฒ˜๋Ÿผ error ๋ฐœ์ƒ ์‹œ, catch ํ•˜๋ ค๋ฉด

=> use try...catch block!

// try...catch on Async/Await
const urls = [
  'https://jsonplaceholder.typicode.com/users',
  'weird url',    // ์—ฌ๊ธฐ์„œ ์—๋Ÿฌ๋‚ ๊ฑฐ์ž„
  'https://jsonplaceholder.typicode.com/albums',
];

const getData = async function () {
  try {

    const [users, posts, albums] =
      await Promise.all(urls.map(url =>
        fetch(url).then(resp => resp.json())
      ));

    console.log("Users  : ", users);
    console.log("Posts  : ", posts);
    console.log("Albums  : ", albums);

  } catch (err) {

    console.log("Oooops!", err)

  }
}

// --output-- => Oooops! TypeError: Failed to fetch

async/await์€ ์ƒˆ๋กœ์šด ์‹ ํƒ์Šค๋‹ˆ๊นŒ although intimidating at first ํ•˜๊ธด ํ•˜์ง€๋งŒ
์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ!!

โ€‹

137. ES9 (ES2018)

์ „ ๋ฒ„์ „์— ๋น„ํ•ด์„œ ์•Œ์•„์•ผํ•  three main new features ๊ฐ€ ์žˆ๋‹ค

Object spread operator

//Object spread operator
const animals = {
  tiger: 23,
  lion: 5,
  monkey: 2,
  quokka: 1204
}

const { tiger, ...restAnimals, quokka } = animals;
 // SyntaxError: Rest element must be last element

const { tiger, ...restAnimals } = animals;
// --output--
// tiger -> 23
// restAnimals -> {lion: 5, monkey: 2, quokka: 1204}
function objectSpread(p1, p2, p3) {
  console.log(p1);
  console.log(p2);
  console.log(p3);
}

const { tiger, lion, ...restAnimals } = animals;

objectSpread(tiger, lion, restAnimals);
// --output--
// 23
// 5
// {monkey: 2, quokka: 1204}

โ€‹

138. ES9 (ES2018) - Async

finally

const urls = [
  'http://swapi.dev/api/people/1/',
  'http://swapi.dev/api/people/2/',
  'http://swapi.dev/api/people/3/',
  'http://swapi.dev/api/people/4/',
]

Promise.all(urls.map(url =>
  fetch(url).then(person => person.json())
))
  .then(people => {
    console.log('1', people[0]);
    throw Error;
    console.log('2', people[1]);
    console.log('3', people[2]);
    console.log('4', people[3]);
  })
  .catch(err => console.log('ughhhhh fix it!', err))
  .finally(() => console.log('extra'))

// --output--
// 1 {name: "Luke Skywalker", height: "172", mass: "77", hair_color: "blond", skin_color: "fair", …}
// ughhhhh fix it! ƒ Error() { [native code] }
// extra

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally

finally() ๋ฉ”์†Œ๋“œ๋Š” Promise ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
Promise๊ฐ€ ์ฒ˜๋ฆฌ๋˜๋ฉด ์ถฉ์กฑ๋˜๊ฑฐ๋‚˜ ๊ฑฐ๋ถ€๋˜๋Š”์ง€ ์—ฌ๋ถ€์— ๊ด€๊ณ„์—†์ด ์ง€์ •๋œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
์ด๊ฒƒ์€ Promise๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ˆ˜ํ–‰ ๋˜์—ˆ๋Š”์ง€ ๊ฑฐ์ ˆ๋˜์—ˆ๋Š”์ง€์— ๊ด€๊ณ„์—†์ด Promise๊ฐ€ ์ฒ˜๋ฆฌ ๋œ ํ›„์— ์ฝ”๋“œ๊ฐ€ ๋ฌด์กฐ๊ฑด ํ•œ ๋ฒˆ์€ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

 

์š”๊ฒƒ์„ ์“ฐ๋Š” ์ด์œ  : Promise์˜ then()๊ณผ catch() ํ•ธ๋“ค๋Ÿฌ์—์„œ์˜ ์ฝ”๋“œ ์ค‘๋ณต์„ ํ”ผํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

 

p.then(onFulfilled, onRejected) ์ธ ๊ฒƒ๊ณผ ๋‹ฌ๋ฆฌ
p.finally(onFinally) ์š”๋ ‡๊ฒŒ ์“ฐ์ž„.


onFinally์— ๋“ค์–ด๊ฐ€๋Š” function์€ ์–ด๋– ํ•œ ์ธ์ˆ˜๋„ ์ „๋‹ฌ๋ฐ›์ง€ ์•Š๋Š”๋‹ค.
์™œ๋ƒํ•˜๋ฉด ์ดํ–‰/๊ฑฐ๋ถ€ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์—!

โ€‹

for await of

const urls = [
  'https://jsonplaceholder.typicode.com/users',
  'https://jsonplaceholder.typicode.com/posts',
  'https://jsonplaceholder.typicode.com/albums',
];

const getData = async function () {
  try {
    const [users, posts, albums] =
      await Promise.all(urls.map(url => {
        const response = await fetch(url);
        return response.json();
      }));

    console.log("Users  : ", users);
    console.log("Posts  : ", posts);
    console.log("Albums  : ", albums);
  } catch (err) {
    console.log('Ooooops!!', err);
  }
}

// do exactly what this one does above by 
// using by "for await of"
const getData2 = async function () {
  const arrayOfPromises = urls.map(url => fetch(url));

  for await (let request of arrayOfPromises) {
    const data = await request.json();
    console.log(data);
  }
}

โ€‹

Promise.all

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

๋ฐ˜ํ™˜ํ•œ ํ”„๋กœ๋ฏธ์Šค์˜ ์ดํ–‰ ๊ฒฐ๊ณผ๊ฐ’์€ (ํ”„๋กœ๋ฏธ์Šค๊ฐ€ ์•„๋‹Œ ๊ฐ’์„ ํฌํ•จํ•˜์—ฌ) ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ฃผ์–ด์ง„ ์ˆœํšŒ ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด์— ํฌํ•จ๋œ ๋ชจ๋“  ๊ฐ’์„ ๋‹ด์€ ๋ฐฐ์—ด์ž…๋‹ˆ๋‹ค.

์ฃผ์–ด์ง„ ํ”„๋กœ๋ฏธ์Šค ์ค‘ ํ•˜๋‚˜๋ผ๋„ ๊ฑฐ๋ถ€ํ•˜๋ฉด, ๋‹ค๋ฅธ ํ”„๋กœ๋ฏธ์Šค์˜ ์ดํ–‰ ์—ฌ๋ถ€์— ์ƒ๊ด€์—†์ด ์ฒซ ๋ฒˆ์งธ ๊ฑฐ๋ถ€ ์ด์œ ๋ฅผ ์‚ฌ์šฉํ•ด ๊ฑฐ๋ถ€ํ•ฉ๋‹ˆ๋‹ค.

Promise.all์€ ์ฃผ์–ด์ง„ ์ˆœํšŒ ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด๊ฐ€ ๋น„์–ด์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ ๋™๊ธฐ์ ์œผ๋กœ ์ดํ–‰๋ฉ๋‹ˆ๋‹ค.

var p = Promise.all([]); // ์ฆ‰์‹œ ์ดํ–‰ํ•จ
var p2 = Promise.all([1337, "hi"]); // ํ”„๋กœ๋ฏธ์Šค๊ฐ€ ์•„๋‹Œ ๊ฐ’์€ ๋ฌด์‹œํ•˜์ง€๋งŒ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋จ
console.log(p);
console.log(p2);
setTimeout(function() {
    console.log('the stack is now empty');
    console.log(p2);
});

// ์ถœ๋ ฅ
// Promise { <state>: "fulfilled", <value>: Array[0] } // p
// Promise { <state>: "pending" } // p2
// the stack is now empty
// Promise { <state>: "fulfilled", <value>: Array[2] } // p2

--- MDN ์˜ˆ์ œ๊ฐ€ ๋งค์šฐ ๋„์›€์ด ๋˜๋Š”๊ตฌ๋งŒโ˜บ

โ€‹

139. Job Queue

Promise๋Š” ์ตœ๊ทผ์— ์ถ”๊ฐ€๋˜์–ด์„œ
์ด ์ถ”๊ฐ€์‚ฌํ•ญ์„ ์ˆ˜์šฉaccommodate ํ•˜๊ธฐ ์œ„ํ•ด์„œ
๊ธฐ์กด์˜ Event Loop๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผํ–ˆ๋‹ค.

ECMA script์—์„œ๋Š” Promise ๋ฅผ ์œ„ํ•ด์„œ ์›๋ž˜ ๊ฐ€์ง€๊ณ  ์žˆ๋˜ Callback Queue ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ๋Œ€์‹ ์— ๋‹ค๋ฅธ ํ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๊ณ  ํ–ˆ๋‹ค.

๊ทธ๊ฑฐ์Šจ ๋ฐ”๋กœ ‘Job Queue’์ด๋‹ค! (ํ˜น์€ Microtask Queue)

 

Job Queue๋Š” ์ฝœ๋ฐฑํ์— ๋น„ํ•ด์„œ smaller ํ•˜์ง€๋งŒ ๋” ๋†’์€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ฐ€์ง„๋‹ค.
(Event Loop๊ฐ€ ์ฒ˜๋ฆฌํ•  ๋•Œ)

 

JAVASCRIPT RUNTIME ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๋‹ค์‹œ ๋ณด์ž

 

 

Event Loop ๋Š” ๋จผ์ € Jop Queue ๋ฅผ ์ฒดํ‚นํ•˜๊ณ 
์ฝœ์Šคํƒ์ด ๋น„์—ˆ์„๋•Œ ์š”๊ฑฐ ๋จผ์ € ์ฒ˜๋ฆฌํ•œ๋‹ค.

// Callback Queue
setTimeout(() => {
  console.log('1', 'is the loneliest number')
}, 0);
setTimeout(() => {
  console.log('2', 'can be as bad as one')
}, 0);

// 2 Job Queue - Microtask Queue
Promise.resolve('hi').then((data) => console.log('2', data));

// 3
console.log('3', 'is a crowd');

---output---

3 is a crowd >> ๋น„๋™๊ธฐ๊ฐ€ ์•„๋‹Œ ์• ๋Š” ๋จผ์ € ์ฒ˜๋ฆฌ๋˜๊ณ 
2 hi >> ์ด๋ฒคํŠธํ: ํ—ค์ด์žกํ!! ๋ญ๊ฐ–๊ณ ์žˆ๋Š”๊ฑฐ์žˆ๋‹ˆ? / ์žกํ: ใ…‡ใ…‡์ด๊ฑฐ์žˆ์—‰
undefined >> JS file ๋—
1 is the loneliest number >> ์ด๋ฒคํŠธํ: ํ—ค์ด์žกํ!! ๋ญ๊ฐ–๊ณ ์žˆ๋Š”๊ฑฐ์žˆ๋‹ˆ? / ์žกํ: ใ„ดใ„ด์—†์—‰!! ์ด์ œ ์ฝœ๋ฐฑํํ•œํ…Œ ๊ฐ€๋„๋ผ!!
2 can be as bad as one

์–ด๋–ค ๋ ˆ๊ฑฐ์‹œ ๋ธŒ๋ผ์šฐ์ €๋Š” ์žกํ๊ฐ€ ์—†๊ธฐ๋„ํ•˜์ง€๋งŒ ๋Œ€๋ถ€๋ถ„ ์Šคํƒ ๋‹ค๋“œ๋ฅผ ๋”ฐ๋ฅธ๋‹ค.

โ€‹

140. Parallel, Sequence and Race

multiple promises๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ ์žˆ์–ด์„œ ์„ธ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

  1. in parallel (start all at the same time)
  2. sequential (they're all dependent on each other)
  3. race (one comes back first and ignores the rest.)

โ€‹

const promisify = (item, delay) =>
  new Promise((resolve) =>
    setTimeout(() =>
      resolve(item), delay));

const a = () => promisify('a', 100);
const b = () => promisify('b', 5000);
const c = () => promisify('c', 3000);

์œ„์˜ 3๊ฐœ์˜ multiple promises๋ฅผ ๊ฐ ๋ฐฉ๋ฒ•์œผ๋กœ ์ฒ˜๋ฆฌํ•ด๋ณด์ž

โ€‹

1) Parallel

async function parallel() {
  const promises = [a(), b(), c()];
  const [output1, output2, output3] = await Promise.all(promises);
  return `parallel is done: ${output1} ${output2} ${output3}`
}

parallel().then(console.log);

// --output--
// (0์ดˆํ›„) Promise {<pending>}
// (5์ดˆํ›„) parallel is done: a b c

โ€‹

2) Race

async function race() {
  const promises = [a(), b(), c()];
  const output1 = await Promise.race(promises);
  return `race is done: ${output1}`;
}

race().then(console.log);

// -- output --
// (  0์ดˆํ›„) Promise {<pending>}
// (0.1์ดˆํ›„) race is done: a

โ€‹

3) Sequential

async function sequence() {
  const output1 = await a();
  const output2 = await b();
  const output3 = await c();
  return `sequence is done ${output1} ${output2} ${output3}`
}

sequence().then(console.log);
// -- output --
// (  0์ดˆํ›„) Promise {<pending>}
// (8.1์ดˆํ›„) sequence is done a b c

โ€‹

+ ์…‹ ๋‹ค ํ•œ๊บผ๋ฒˆ์— ํ•˜๋ฉด...?

parallel().then(console.log);
sequence().then(console.log);
race().then(console.log);

// -- output --
// (  0์ดˆํ›„) 01:12:56.033 Promise {<pending>}
// (0.1์ดˆํ›„) 01:12:56.139 race is done: a
// (  5์ดˆํ›„) 01:13:01.033 parallel is done: a b c
// (8.1์ดˆํ›„) 01:13:04.166 sequence is done a b c

โ€‹

 

 

์ถœ์ฒ˜ : Udemy - JavaScript: The Advanced Concept
https://www.udemy.com/course/advanced-javascript-concepts

๋Œ“๊ธ€