Asynchronous JavaScript: Promises, Async/Await, and AJAX

vancouverยท2023๋…„ 7์›” 11์ผ
0

javascript์ดํ•ดํ•˜๊ธฐ

๋ชฉ๋ก ๋ณด๊ธฐ
21/22

Asynchronous JavaScript, AJAX and APIs

SYNCHRONOUS (๋™๊ธฐ์ )
๐Ÿ‘‰ ๋Œ€๋ถ€๋ถ„์˜ ์ฝ”๋“œ๋Š” ๋™๊ธฐ์ (Synchronous)์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
๐Ÿ‘‰๋™๊ธฐ์ ์ธ ์ฝ”๋“œ๋Š” ํ•œ ์ค„์”ฉ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
๐Ÿ‘‰๊ฐ ์ฝ”๋“œ ๋ผ์ธ์€ ์ด์ „ ๋ผ์ธ์˜ ์‹คํ–‰์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค.
๐Ÿ‘Ž ์‹คํ–‰ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์€ ์ฝ”๋“œ ์‹คํ–‰์„ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค.

ASYNCHRONOUS (๋น„๋™๊ธฐ์ )

๐Ÿ‘‰ ๋น„๋™๊ธฐ์ (Asynchronous) ์ฝ”๋“œ๋Š” "๋ฐฑ๊ทธ๋ผ์šด๋“œ"์—์„œ ์‹คํ–‰๋˜๋Š” ์ž‘์—…์ด ์™„๋ฃŒ๋œ ํ›„์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
๐Ÿ‘ ๋น„๋™๊ธฐ์ ์ธ ์ฝ”๋“œ๋Š” ๋…ผ๋ธ”๋กœํ‚น(Non-blocking)์ž…๋‹ˆ๋‹ค.
๐Ÿ‘‰ ์‹คํ–‰์€ ๋น„๋™๊ธฐ์ ์ธ ์ž‘์—…์ด ์ž‘์—…์„ ์™„๋ฃŒํ•  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
๐Ÿ‘‰ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์ž์ฒด๋งŒ์œผ๋กœ๋Š” ์ฝ”๋“œ๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

AJAX
๋น„๋™๊ธฐ JavaScript์™€ XML(AJAX)์€ ์›๊ฒฉ ์›น ์„œ๋ฒ„์™€ ๋น„๋™๊ธฐ์ ์œผ๋กœ ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค. AJAX๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์›น ์„œ๋ฒ„์— ๋ฐ์ดํ„ฐ๋ฅผ ๋™์ ์œผ๋กœ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

AJAX ํ˜ธ์ถœ์„ ํ†ตํ•ด ์›น ์„œ๋ฒ„์— HTTP ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  XML, JSON, HTML๊ณผ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ํ˜•์‹์œผ๋กœ ์‘๋‹ต์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ „์ฒด ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜์ง€ ์•Š๊ณ ๋„ ์›น ํŽ˜์ด์ง€์˜ ํŠน์ • ๋ถ€๋ถ„์„ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์–ด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๋” ์ƒํ˜ธ์ž‘์šฉ์ ์ด๊ณ  ๋ฐ˜์‘ํ˜•์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ‘‰API(Application Programming Interface): ์†Œํ”„ํŠธ์›จ์–ด ์กฐ๊ฐ์œผ๋กœ, ๋‹ค๋ฅธ ์†Œํ”„ํŠธ์›จ์–ด๊ฐ€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋œ ๊ฒƒ์œผ๋กœ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๋“ค์ด ์„œ๋กœ ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ‘‰์›น ๊ฐœ๋ฐœ์—์„œ๋Š” ๋‹ค์–‘ํ•œ ์œ ํ˜•์˜ API๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

DOM APIGeolocation APIOwn Class API"Online" API

๐Ÿ‘‰"์˜จ๋ผ์ธ" API: ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ, ๋ฐ์ดํ„ฐ ์š”์ฒญ์„ ๋ฐ›๊ณ  ์‘๋‹ต์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ‘‰์šฐ๋ฆฌ๋Š” ์ง์ ‘ ์›น API๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ด ํ•„์š”ํ•˜๋ฉฐ, ์˜ˆ๋ฅผ ๋“ค์–ด node.js๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค) ๋˜๋Š” 3rd-party API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Weather data
  • Data about countries
  • Flights data
  • Currency conversion data
  • APIs for sending email or SMS
  • Google Maps
  • Millions of possibilities.

Our First AJAX Call: XMLHttpRequest

const btn = document.querySelector(".btn-country");
const countriesContainer = document.querySelector(".countries");

///////////////////////////////////////

const getCountryData = function (country) {
  const request = new XMLHttpRequest();
  request.open(`GET`, `https://restcountries.com/v3.1/name/${country}`);
  request.send();
  console.log(request.responseText);

  request.addEventListener(`load`, function () {
    const [data] = JSON.parse(this.responseText);
    console.log(data);

    const lang = Object.values(data.languages);
    // languages: {por: 'Portuguese'}
	// languages: {kor: 'Korean'} ์†์„ฑ์˜ ๊ฐ’์„ ๊บผ๋‚ด๊ธฐ์œ„ํ•จ.
    // ์ถœ๋ ฅ: Korean, Portuguese
	const curr = Object.values(data.currencies)[0].name;
    // currencies: KRW: {name: 'South Korean won', symbol: 'โ‚ฉ'}
	// currencies: EUR: {name: 'Euro', symbol: 'โ‚ฌ'} name์˜ ๊ฐ’์„ ๊บผ๋‚ด๊ธฐ์œ„ํ•จ.
    // ์ถœ๋ ฅ: South Korean won, Euro

    const html = `
  <article class="country">
    <img class="country__img" src="${data.flags.svg}" />
    <div class="country__data">
      <h3 class="country__name">${data.name.common}</h3>
      <h4 class="country__region">${data.region}</h4>
      <p class="country__row"><span>๐Ÿ‘ซ</span>${(
        +data.population / 1000000
      ).toFixed(1)}</p>
      <p class="country__row"><span>๐Ÿ—ฃ๏ธ</span>${lang}
      </p>
      <p class="country__row"><span>๐Ÿ’ฐ</span>${curr}</p>
    </div>
  </article>`;
    countriesContainer.insertAdjacentHTML(`beforeend`, html);
    countriesContainer.style.opacity = 1;
  });
};
getCountryData(`Korea`);
getCountryData(`portugal`);

[OPTIONAL] How the Web Works: Requests and Responses

GET /rest/v2/alpha/PT HTTP/1.1 ---> ์‹œ์ž‘ ๋ผ์ธ: HTTP ๋ฉ”์†Œ๋“œ + ์š”์ฒญ ๋Œ€์ƒ + HTTP ๋ฒ„์ „

Host: www.google.com
User-Agent: Mozilla/5.0 ---> HTTP ์š”์ฒญ ํ—ค๋” (๋‹ค์–‘ํ•œ ๊ฐ€๋Šฅ์„ฑ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค)
Accept-Language: en-US

<BODY> ----> ์š”์ฒญ ๋ณธ๋ฌธ (์„œ๋ฒ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•  ๋•Œ๋งŒ ํฌํ•จ๋˜๋ฉฐ, ์˜ˆ๋ฅผ ๋“ค์–ด POST ์š”์ฒญ ๋“ฑ)


HTTP/1.1 200 OK ---> ์‹œ์ž‘ ๋ผ์ธ: HTTP ๋ฒ„์ „ + ์ƒํƒœ ์ฝ”๋“œ + ์ƒํƒœ ๋ฉ”์‹œ์ง€

Date: Fri, 18 Jan 2021
Content-Type: text/html ----> HTTP ์‘๋‹ต ํ—ค๋” (๋‹ค์–‘ํ•œ ๊ฐ€๋Šฅ์„ฑ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค)
Transfer-Encoding: chunked

<BODY> ----> ์‘๋‹ต ๋ณธ๋ฌธ (๋Œ€๋ถ€๋ถ„์˜ ์‘๋‹ต์— ํฌํ•จ๋ฉ๋‹ˆ๋‹ค)

Welcome to Callback Hell

neighbour Country

const renderCountry = function (data, className = ``) {
  const lang = Object.values(data.languages);
  const curr = Object.values(data.currencies)[0].name;

  const html = `
    <article class="country ${className}">
      <img class="country__img" src="${data.flags.svg}" />
      <div class="country__data">
        <h3 class="country__name">${data.name.common}</h3>
        <h4 class="country__region">${data.region}</h4>
        <p class="country__row"><span>๐Ÿ‘ซ</span>${(
          +data.population / 1000000
        ).toFixed(1)}</p>
        <p class="country__row"><span>๐Ÿ—ฃ๏ธ</span>${lang}
        </p>
        <p class="country__row"><span>๐Ÿ’ฐ</span>${curr}</p>
      </div>
    </article>`;
  countriesContainer.insertAdjacentHTML(`beforeend`, html);
  countriesContainer.style.opacity = 1;
};

const getCountryAndNeighbour = function (country) {
  // AJAX call country 1
  const request = new XMLHttpRequest();
  request.open(`GET`, `https://restcountries.com/v3.1/name/${country}`);
  request.send();
  console.log(request.responseText);

  request.addEventListener(`load`, function () {
    const [data] = JSON.parse(this.responseText);
    console.log(data);

    // Render Country  1
    renderCountry(data);

    // Get neighbour country (2)
    const neighbour = data.borders?.[0];

    if (!neighbour) return;
    // AJAX call country 2
    const request2 = new XMLHttpRequest();
    request2.open(`GET`, `https://restcountries.com/v3.1/alpha/${neighbour}`);
    request2.send();

    request2.addEventListener(`load`, function () {
      const [data2] = JSON.parse(this.responseText);
      console.log(data2);

      renderCountry(data2, `neighbour`);
    });
  });
};

getCountryAndNeighbour(`Korea`);
// getCountryAndNeighbour(`portugal`);

Callback hell

/// Callback Hell (โŒBad Code)
setTimeout(() => {
  console.log(`1 second passed`); // 1์ดˆํ›„ ์ถœ๋ ฅ
  setTimeout(() => {
    console.log(`2 second passed`); //  2์ดˆํ›„ ์ถœ๋ ฅ
    setTimeout(() => {
      console.log(`3 second passed`); // 3์ดˆํ›„ ์ถœ๋ ฅ
    }, 1000);
  }, 1000);
}, 1000);

Promises and the Fetch API (ES6)

๐Ÿ‘‰ Promise: ๋น„๋™๊ธฐ ์ž‘์—…์˜ ๊ฒฐ๊ณผ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.

๐Ÿ‘‡ ์ข€ ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ

๐Ÿ‘‰ Promise: ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ „๋‹ฌ๋œ ๊ฐ’์„ ๋‹ด๋Š” ์ปจํ…Œ์ด๋„ˆ์ž…๋‹ˆ๋‹ค.

๐Ÿ‘‡ ์ข€ ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ

๐Ÿ‘‰ Promise: ๋ฏธ๋ž˜์˜ ๊ฐ’์„ ๋‹ด๋Š” ์ปจํ…Œ์ด๋„ˆ์ž…๋‹ˆ๋‹ค.

๐Ÿ‘‰ ์ด์ œ ์šฐ๋ฆฌ๋Š” ๋น„๋™๊ธฐ ํ•จ์ˆ˜์— ์ „๋‹ฌ๋œ ์ด๋ฒคํŠธ์™€ ์ฝœ๋ฐฑ์— ์˜์กดํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
๐Ÿ‘‰ ์ฝœ๋ฐฑ์„ ์ค‘์ฒฉํ•˜๋Š” ๋Œ€์‹ , ์šฐ๋ฆฌ๋Š” ์—ฐ์†์ ์ธ ๋น„๋™๊ธฐ ์ž‘์—…์„ ์œ„ํ•ด ํ”„๋กœ๋ฏธ์Šค๋ฅผ ์ฒด์ธ์œผ๋กœ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฝœ๋ฐฑ ์ง€์˜ฅ์—์„œ ๋ฒ—์–ด๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

Consuming Promises

// traditional 
const getCountryData = function (country) {
  const request = new XMLHttpRequest();
  request.open(`GET`, `https://restcountries.com/v3.1/name/${country}`);
  request.send();
  console.log(request.responseText);

  request.addEventListener(`load`, function () {
    const [data] = JSON.parse(this.responseText);
    console.log(data);
////////////////////////////////////////// Promise๋ฅผ ํ†ตํ•œ ์ฝ”๋“œ ๊ฐ„๊ฒฐํ™”
/// Promise
// const getCountryData = function (country) {
//   fetch(`https://restcountries.com/v3.1/name/${country}`)
//     .then(function (response) {
//       console.log(response);
//       return response.json();
//     })
//     .then(function (data) {
//       console.log(data);
//       renderCountry(data[0]);
//     });
// };

// getCountryData(`south korea`);

/// Promise (arrow function)
const getCountryData = function (country) {
  fetch(`https://restcountries.com/v3.1/name/${country}`)
    .then((response) => response.json())
    .then((data) => renderCountry(data[0]));
};
getCountryData(`south korea`); 

Chaining Promises

const getCountryData = function (country) {
  // Country 1
  fetch(`https://restcountries.com/v3.1/name/${country}`)
    .then((response) => response.json())
    .then((data) => {
      renderCountry(data[0]);
      const neighbour = data[0].borders?.[0];

      if (!neighbour) return;

      // Country 2
      return fetch(`https://restcountries.com/v3.1/alpha/${neighbour}`);
    })
    .then((response) => response.json())
    .then(([data]) => renderCountry(data, `neighbour`));
    
};
getCountryData(`south korea`);

Handling Rejected Promises

finally Method

Promise ์ฒด์ธ์—์„œ ๋งˆ์ง€๋ง‰์œผ๋กœ ํ•ญ์ƒ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ •์˜ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ, finally ๋ฉ”์„œ๋“œ๋Š” ์„ฑ๊ณต ๋˜๋Š” ์‹คํŒจ ์—ฌ๋ถ€์— ๊ด€๊ณ„์—†์ด ํ•ญ์ƒ ํ˜ธ์ถœ

catch Method

Promise๋Š” ๋น„๋™๊ธฐ ์ž‘์—…์˜ ์™„๋ฃŒ ๋˜๋Š” ์‹คํŒจ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ์ฒด, catch ๋ฉ”์„œ๋“œ๋Š” Promise ์ฒด์ธ์—์„œ ์ด์ „ Promise์—์„œ ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ

const getCountryData = function (country) {
  // Country 1
  fetch(`https://restcountries.com/v3.1/name/${country}`)
    .then((response) => response.json())
    .then((data) => {
      renderCountry(data[0]);
      const neighbour = data[0].borders?.[0];

      if (!neighbour) return;

      // Country 2
      return fetch(`https://restcountries.com/v3.1/alpha/${neighbour}`);
    })
    .then((response) => response.json())
    .then(([data]) => renderCountry(data, `neighbour`))
    .catch((err) => { 
      console.error(`${err}๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ`);
      renderError(`Something went wrong ๐Ÿ’ฅ๐Ÿ’ฅ ${err.message}. Try again!`);
    })
    .finally(() => { 
      countriesContainer.style.opacity = 1;
    });
};

btn.addEventListener(`click`, function () {
  getCountryData(`south korea`);
});

๋„คํŠธ์›Œํฌ๊ฐ€ ์—ฐ๊ฒฐ์ด ์•ˆ๋ ๋•Œ๋ฅผ ๊ฐ€์ •ํ•˜์—ฌ ํ…Œ์ŠคํŠธํ•˜์˜€๋‹ค.

Throwing Errors Manually

throw
JavaScript์—์„œ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๊ตฌ๋ฌธ.
์˜ˆ์™ธ๋ž€ ์ฝ”๋“œ ์‹คํ–‰ ์ค‘์— ๋ฐœ์ƒํ•˜๋Š” ์˜ค๋ฅ˜๋‚˜ ์˜ˆ๊ธฐ์น˜ ์•Š์€ ์ƒํ™ฉ.
throw ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ฝ”๋“œ ๋ธ”๋ก์œผ๋กœ ์ด๋™.

const getJSON = function (url, errorMsg = `Something went wrong`) {
  return fetch(url).then((response) => {
    if (!response.ok) throw new Error(`${errorMsg} (${response.status})`);

    return response.json();
  });
};  // getJSON์ด๋ผ๋Š” ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ฌ์œผ๋กœ์จ ๊ฐ„ํŽธํ™”

const getCountryData = function (country) {
  // Country 1
  getJSON(`https://restcountries.com/v3.1/name/${country}`, `Country not found`)
    .then((data) => {
      renderCountry(data[0]);
      const neighbour = data[0].borders?.[0];

      if (!neighbour) throw new Error(`No neighbour found!`);

      // Country 2
      return getJSON(
        `https://restcountries.com/v3.1/alpha/${neighbour}`,
        `Country not found`
      );
    })
    .then(([data]) => renderCountry(data, `neighbour`))
    .catch((err) => {
      console.error(`${err}๐Ÿ’ฅ๐Ÿ’ฅ๐Ÿ’ฅ`);
      renderError(`Something went wrong ๐Ÿ’ฅ๐Ÿ’ฅ ${err.message}. Try again!`);
    })
    .finally(() => {
      countriesContainer.style.opacity = 1;
    });
};

btn.addEventListener(`click`, function () {
  getCountryData(`south korea`);
});
getCountryData(`australia`);

์ด์›ƒ๋‚˜๋ผ๊ฐ€ ์—†๋Š” ๋‚˜๋ผ๋ฅผ ์„ ํƒํ•œ๊ฑธ ๊ฐ€์ •ํ•˜์˜€์„๋•Œ.

Coding Challenge #1

// Coding Challenge #1

// Test data:
// Coordinates 1: 52.508, 13.381 (Latitude, Longitude)
// Coordinates 2: 19.037, 72.873
// Coordinates 3: -33.933, 18.474

// ์ด ๋„์ „์—์„œ๋Š” 'whereAmI'๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•˜์—ฌ GPS ์ขŒํ‘œ์— ๊ธฐ๋ฐ˜ํ•˜์—ฌ ๊ตญ๊ฐ€๋งŒ์„ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
// ์ด๋ฅผ ์œ„ํ•ด ์ขŒํ‘œ๋ฅผ ์ง€์˜ค์ฝ”๋”ฉํ•˜๋Š” ๋ฐ ๋‘ ๋ฒˆ์งธ API๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด ๋„์ „์—์„œ๋Š” ์ฒ˜์Œ์œผ๋กœ ์ง์ ‘ API๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

/* 1. 'whereAmI' ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์œ„๋„('lat')์™€ ๊ฒฝ๋„('lng') ๊ฐ’์„ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค. ์ด ๊ฐ’๋“ค์€ GPS ์ขŒํ‘œ์ด๋ฉฐ, ์•„๋ž˜์˜ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ์— ์˜ˆ์‹œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. */

/* 2. ์ œ๊ณต๋œ ์ขŒํ‘œ์˜ "์—ญ ์ง€์˜ค์ฝ”๋”ฉ"์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์—ญ ์ง€์˜ค์ฝ”๋”ฉ์€ ์ขŒํ‘œ๋ฅผ ์˜๋ฏธ์žˆ๋Š” ์œ„์น˜๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋„์‹œ์™€ ๊ตญ๊ฐ€ ์ด๋ฆ„๊ณผ ๊ฐ™์€ ์ •๋ณด์ž…๋‹ˆ๋‹ค. 
์—ญ ์ง€์˜ค์ฝ”๋”ฉ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ API๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค: https://geocode.xyz/api. ์ด URL ํ˜•์‹์œผ๋กœ AJAX ํ˜ธ์ถœ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค: https://geocode.xyz/52.508,13.381?geoit=json. 
fetch API์™€ ํ”„๋กœ๋ฏธ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. "getJSON" ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋„๋ก ์ฃผ์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋ถ€์ •ํ–‰์œ„์ž…๋‹ˆ๋‹ค.*/

/* 3. ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ ํ›„, ์ฝ˜์†”์—์„œ ํ•ด๋‹น ์œ„์น˜์— ๋Œ€ํ•œ ๋ชจ๋“  ์†์„ฑ์„ ํ™•์ธํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ดํŽด๋ด…๋‹ˆ๋‹ค. 
๊ทธ๋Ÿฐ ๋‹ค์Œ ์ด ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ˜์†”์— ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค: "You are in Berlin, Germany"์™€ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.*/

/* 4. ํ”„๋กœ๋ฏธ์Šค ์ฒด์ธ์˜ ๋์— .catch ๋ฉ”์„œ๋“œ๋ฅผ ์—ฐ๊ฒฐํ•˜์—ฌ ์—๋Ÿฌ๋ฅผ ์ฝ˜์†”์— ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. */

/* 5. ์ด API๋Š” ์ดˆ๋‹น 3๊ฐœ์˜ ์š”์ฒญ๋งŒ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋น ๋ฅด๊ฒŒ ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ์ฝ”๋“œ 403์™€ ํ•จ๊ป˜ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 
์ด๋Š” ์š”์ฒญ์— ๋Œ€ํ•œ ์—๋Ÿฌ์ž…๋‹ˆ๋‹ค. ๊ธฐ์–ตํ•˜์„ธ์š”, ์ด ๊ฒฝ์šฐ fetch()๋Š” ํ”„๋กœ๋ฏธ์Šค๋ฅผ ๊ฑฐ๋ถ€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ์˜๋ฏธ ์žˆ๋Š” ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ง์ ‘ ํ”„๋กœ๋ฏธ์Šค๋ฅผ ๊ฑฐ๋ถ€ํ•˜๋Š” ์—๋Ÿฌ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. */

// Part 2

/* 6. ์ด์ œ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตญ๊ฐ€๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ง€์˜ค์ฝ”๋”ฉ API ๊ฒฐ๊ณผ์—์„œ ๊ด€๋ จ ์†์„ฑ์„ ๊ฐ€์ ธ์™€ ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋˜ ๊ตญ๊ฐ€ API์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.*/

/* 7. ๋งˆ์ง€๋ง‰ ๊ฐ•์˜์—์„œ ์ˆ˜ํ–‰ํ•œ ๊ฒƒ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ตญ๊ฐ€๋ฅผ ๋ Œ๋”๋งํ•˜๊ณ  ๋ฐœ์ƒํ•˜๋Š” ๋ชจ๋“  ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. (์‹ฌ์ง€์–ด ์ฝ”๋“œ๋ฅผ ๋ณต์‚ฌํ•ด๋„ ๋ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ์ž…๋ ฅํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค) */

const renderCountry = function (data, className = ``) {
  const lang = Object.values(data.languages);
  const curr = Object.values(data.currencies)[0].name;

  const html = `
        <article class="country ${className}">
          <img class="country__img" src="${data.flags.svg}" />
          <div class="country__data">
            <h3 class="country__name">${data.name.common}</h3>
            <h4 class="country__region">${data.region}</h4>
            <p class="country__row"><span>๐Ÿ‘ซ</span>${(
              +data.population / 1000000
            ).toFixed(1)}</p>
            <p class="country__row"><span>๐Ÿ—ฃ๏ธ</span>${lang}
            </p>
            <p class="country__row"><span>๐Ÿ’ฐ</span>${curr}</p>
          </div>
        </article>`;
  countriesContainer.insertAdjacentHTML(`beforeend`, html);
  countriesContainer.style.opacity = 1;
};

const renderError = function (msg) {
  countriesContainer.insertAdjacentText(`beforeend`, msg);
  countriesContainer.style.opacity = 1;
};

const getJSON = function (url, errorMsg = `Something went wrong`) {
  return fetch(url).then((response) => {
    if (!response.ok) throw new Error(`${errorMsg} (${response.status})`);

    return response.json();
  });
};

const whereAmI = function (lat, lng) {
  fetch(
    `https://geocode.xyz/${lat},${lng}?geoit=json&auth={my_api}`
  )
    .then((res) => {
      console.log(res);
      if (!res.ok) throw new Error(`Problem with geocoding ${res.status}`);
      return res.json();
    })
    .then((data) => {
      console.log(data);
      console.log(`You are in ${data.city}, ${data.country}`);

      return fetch(`https://restcountries.com/v3.1/name/${data.country}`);
    })
    .then((res) => {
      if (!res.ok) throw new Error(`Country not found (${res.status})`);

      return res.json();
    })
    .then(([data]) => renderCountry(data))
    .catch((err) => console.error(`${err.message}๐Ÿ’ฅ`));
};
btn.addEventListener(`click`, function () {
  whereAmI(52.508, 13.381);
  //   whereAmI(19.037, 72.873);
  whereAmI(-33.933, 18.474);
});

Building a Simple Promise

const lotteryPromise = new Promise(function (resolve, rejcet) {
  console.log(`Lotter draw is happening ๐Ÿ”ฎ`);
  setTimeout(function () {
    if (Math.random() >= 0.5) {
      resolve(`You WIN ๐Ÿ’ฐ`);
    } else {
      rejcet(new Error(`You lost your money ๐Ÿ˜‚`));
    }
  }, 2000);
});

lotteryPromise
  .then((res) => console.log(res))
  .catch((err) => console.error(err));

// Promisifying setTimeout
const wait = function (seconds) {
  return new Promise(function (resolve) {
    setTimeout(resolve, seconds * 1000);
  });
};
wait(1)
  .then(() => {
    console.log(`1 second passed`);
    return wait(1);
  })
  .then(() => {
    console.log(`2 second passed`);
    return wait(1);
  })
  .then(() => {
    console.log(`3 second passed`);
    return wait(1);
  })
  .then(() => console.log(`4 second passed`));

// setTimeout(() => {
//   console.log(`1 second passed`);
//   setTimeout(() => {
//     console.log(`2 second passed`);
//     setTimeout(() => {
//       console.log(`3 second passed`);
//     }, 1000);
//   }, 1000);
// }, 1000);

Promise.resolve(`abc`).then((x) => console.log(x));
Promise.reject(new Error(`Problem!`)).catch((x) => console.error(x));

Promisifying the Geolocation API

const renderCountry = function (data, className = ``) {
  const lang = Object.values(data.languages);
  const curr = Object.values(data.currencies)[0].name;

  const html = `
        <article class="country ${className}">
          <img class="country__img" src="${data.flags.svg}" />
          <div class="country__data">
            <h3 class="country__name">${data.name.common}</h3>
            <h4 class="country__region">${data.region}</h4>
            <p class="country__row"><span>๐Ÿ‘ซ</span>${(
              +data.population / 1000000
            ).toFixed(1)}</p>
            <p class="country__row"><span>๐Ÿ—ฃ๏ธ</span>${lang}
            </p>
            <p class="country__row"><span>๐Ÿ’ฐ</span>${curr}</p>
          </div>
        </article>`;
  countriesContainer.insertAdjacentHTML(`beforeend`, html);
  countriesContainer.style.opacity = 1;
};

const renderError = function (msg) {
  countriesContainer.insertAdjacentText(`beforeend`, msg);
  countriesContainer.style.opacity = 1;
};

const getJSON = function (url, errorMsg = `Something went wrong`) {
  return fetch(url).then((response) => {
    if (!response.ok) throw new Error(`${errorMsg} (${response.status})`);

    return response.json();
  });
};


const getPosition = function () {
  return new Promise(function (resolve, reject) {
    // navigator.geolocation.getCurrentPosition(
    //   (position) => console.log(position),
    //   (err) => console.error(err)
    navigator.geolocation.getCurrentPosition(resolve, reject);
  });
};

getPosition().then((pos) => console.log(pos));

const whereAmI = function () {
  getPosition()
    .then((pos) => {
      const { latitude: lat, longitude: lng } = pos.coords;
      return fetch(
        `https://geocode.xyz/${lat},${lng}?geoit=json&auth=835312574695071493093x6866`
      );
    })

    .then((res) => {
      console.log(res);
      if (!res.ok) throw new Error(`Problem with geocoding ${res.status}`);
      return res.json();
    })
    .then((data) => {
      console.log(data);
      console.log(`You are in ${data.city}, ${data.country}`);

      return fetch(`https://restcountries.com/v3.1/name/${data.country}`);
    })
    .then((res) => {
      if (!res.ok) throw new Error(`Country not found (${res.status})`);

      return res.json();
    })
    .then(([data]) => renderCountry(data))
    .catch((err) => console.error(`${err.message}๐Ÿ’ฅ`));
};
btn.addEventListener(`click`, whereAmI);

Coding Challenge #2

// Coding Challenge #2
/* PART 1
1. 'imgPathโ€™๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›๋Š” โ€˜createImageโ€™ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“œ์„ธ์š”.
2. ์ด ํ•จ์ˆ˜๋Š” ์ƒˆ๋กœ์šด ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ  (document.createElement(โ€˜imgโ€™) ์‚ฌ์šฉ), .src ์†์„ฑ์„ ์ œ๊ณต๋œ ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ๋กœ ์„ค์ •ํ•œ Promise๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
3. ์ด๋ฏธ์ง€ ๋กœ๋”ฉ์ด ์™„๋ฃŒ๋˜๋ฉด, โ€˜imagesโ€™ ํด๋ž˜์Šค๋ฅผ ๊ฐ€์ง„ DOM ์š”์†Œ์— ์ถ”๊ฐ€ํ•˜๊ณ  Promise๋ฅผ resolveํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ fulfilled value๋Š” ์ด๋ฏธ์ง€ ์š”์†Œ ์ž์ฒด์ž…๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€ ๋กœ๋”ฉ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ (error ์ด๋ฒคํŠธ๋ฅผ ๋“ฃ๊ธฐ), Promise๋ฅผ rejectํ•ฉ๋‹ˆ๋‹ค.

PART 2 
4. .then์„ ์‚ฌ์šฉํ•˜์—ฌ Promise๋ฅผ Consumeํ•˜๊ณ  error handler๋„ ์ถ”๊ฐ€ํ•˜์„ธ์š”. 
5. ์ด๋ฏธ์ง€๊ฐ€ ๋กœ๋“œ ๋œ ํ›„, โ€˜waitโ€™ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹คํ–‰์„ 2์ดˆ ๋™์•ˆ ์ผ์‹œ ์ค‘์ง€ํ•˜์„ธ์š”. 
6. 2์ดˆ๊ฐ€ ์ง€๋‚œ ํ›„, ํ˜„์žฌ ์ด๋ฏธ์ง€๋ฅผ ์ˆจ๊ธฐ๊ณ  (display CSS ์†์„ฑ์„ 'noneโ€™์œผ๋กœ ์„ค์ •), ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•˜์„ธ์š”. (ํžŒํŠธ: โ€˜createImageโ€™ Promise์—์„œ ๋ฐ˜ํ™˜๋œ ์ด๋ฏธ์ง€ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ˜„์žฌ ์ด๋ฏธ์ง€๋ฅผ ์ˆจ๊น๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ์ „์—ญ ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.) 
7. ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๊ฐ€ ๋กœ๋“œ ๋œ ํ›„, ๋‹ค์‹œ 2์ดˆ ๋™์•ˆ ์‹คํ–‰์„ ์ผ์‹œ ์ค‘์ง€ํ•˜์„ธ์š”. 
8. 2์ดˆ๊ฐ€ ์ง€๋‚œ ํ›„, ํ˜„์žฌ ์ด๋ฏธ์ง€๋ฅผ ์ˆจ๊ธฐ์„ธ์š”. 

ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ: img ํด๋”์˜ ์ด๋ฏธ์ง€์ž…๋‹ˆ๋‹ค. ์ž˜๋ชป๋œ ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ๋ฅผ ์ „๋‹ฌํ•˜์—ฌ error handler๋ฅผ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”. 
๋„คํŠธ์›Œํฌ ์†๋„๋Š” ๊ฐœ๋ฐœ ๋„๊ตฌ ๋„คํŠธ์›Œํฌ ํƒญ์—์„œ "Fast 3G"๋กœ ์„ค์ •ํ•˜์„ธ์š”. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์ด๋ฏธ์ง€๊ฐ€ ๋„ˆ๋ฌด ๋นจ๋ฆฌ ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค. */

const wait = function (seconds) {
  return new Promise(function (resolve) {
    setTimeout(resolve, seconds * 1000);
  });
};

const imageContainer = document.querySelector(`.images`);
const createImage = function (imgPath) {
  return new Promise(function (resolve, reject) {
    const img = document.createElement(`img`);
    img.src = imgPath;

    img.addEventListener(`load`, function () {
      imageContainer.append(img);
      resolve(img);
    });

    img.addEventListener(`error`, function () {
      reject(new Error(`Image not Found`));
    });
  });
};
let currentImg;
createImage(`img/img-1.jpg`)
  .then((img) => {
    currentImg = img;
    console.log(`Images 1 loaded`);
    return wait(2);
  })
  .then(() => {
    currentImg.style.display = `none`;
    return createImage(`img/img-2.jpg`);
  })
  .then((img) => {
    currentImg = img;
    console.log(`Images 2 loaded`);
    return wait(2);
  })
  .then(() => {
    currentImg.style.display = `none`;
  })
  .catch((err) => console.error(err));

0๊ฐœ์˜ ๋Œ“๊ธ€