비동기 - Async/Await , 병렬적으로 promises 실행하기

Consuming Promises with Async/Await

Async/Await

보기에는 동기적인 것처럼 보이나 사실 behind the scene에서는 비동기적으로 작동한다.
Async와 Await는 promise안에 있는 method위에 뿌려진 syntactic sugar!

await 뒤에는 promise가 나온다.
promise(fetch, json 등 등) 앞에 await를 붙히고 변수에 저장하는 로직

whereAmI 함수 async function으로 만들기

const getPosition = function () {
  return new Promise(function (resolve, reject) {
    navigator.geolocation.getCurrentPosition(resolve, reject);
  });
};

fetch(`https://restcountries.com/v2/name/${country}`).then(res => console.log(res))

const whereAmI = async function () {
  //Geolocation
  const pos = await getPosition();
  const { latitude: lat, longitude: lng } = pos.coords;

  //Reverse Geocoding
  const resGeo = await fetch(`https://geocode.xyz/${lat},${lng}?geoit=json`);
  const dataGeo = await resGeo.json();
  console.log(dataGeo);

  //Country Data
  const res = await fetch(
    `https://restcountries.com/v2/name/${dataGeo.country}`
  );
  console.log(res);
  const data = await res.json();
  renderCountry(data[0]);
};
whereAmI();
console.log('FIRST');

FIRST 가 먼저 콘솔에 출력된다. why? whereAmI는 비동기
// Coding challenge 3// let currentImage;

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

const imagesContainer = document.querySelector('.images');
const createImage = function (imgPath) {
  return new Promise(function (resolve, reject) {
    const img = document.createElement('img');
    img.src = imgPath;
    img.addEventListener('load', () => {
      imagesContainer.appendChild(img);
      resolve(img);
    });
    img.addEventListener('error', err => {
      reject(new Error('Image not found'));
    });
  });
};

Error Handling With try...catch

기본 형태

try {
  let y = 1;
  const x = 2;
  y = 3;
} catch (err) {
  alert(err.message);
}
const whereAmI = async function () {
  try {
    //Geolocation
    const pos = await getPosition();
    const { latitude: lat, longitude: lng } = pos.coords;

    //Reverse Geocoding
    const resGeo = await fetch(`https://geocode.xyz/${lat},${lng}?geoit=json`);
    if (!resGeo.ok) throw new Error('Problem getting location data');

    const dataGeo = await resGeo.json();

    //Country Data
    const res = await fetch(
      `https://restcountries.com/v2/name/${dataGeo.country}`
    );
    if (!resGeo.ok) throw new Error('Problem getting country data');
    const data = await res.json();
    renderCountry(data[0]);

    return `You are in ${dataGeo.city}, ${dataGeo.country}`;
  } catch (err) {
    console.error(`${err.message} happened`);
    renderError(`💥 ${err.message}`);

    //Reject promise returned from async function
    throw err; // err를 throw해줌으로써 아래 then 다음의 코드를 reject로 인식한다.
  }
};

console.log('1 : I will get location');
const city = whereAmI();
console.log(city); //Promise {<pending>} => aysnc function 항상 promise를 return 한다.
//변수안에 저장을 하더라도 string value가 return되지 않음!  string value는 해당 primise의 fulfilled value 가 될 것!

fullfilled value 얻는 법

whereAmI()
  .then(city => console.log(city))
  .catch(err => console.error(`2 : ${err.message}`))
  .finally(() => console.log('3 : Finished getting location'));

Returning Values from Async Functions

(async function () {
  try {
    const city = await whereAmI();
    console.log(city);
  } catch (err) {
    console.error(`2 : ${err.message}`);
  }
  console.log('3 : Finished getting location');
})();

Running Promises in Parallel (병렬적으로)

Promise.all

하나의 promise reject되면 전체 promise가 reject된다.
combinator function 이라고 불리기도 한다. multiple functions를 combine 하므로.

const get3Countries = async function (c1, c2, c3) {
  try {
    // const [data1] = await getJSON(`https://restcountries.com/v2/name/${c1}`);
    // const [data2] = await getJSON(`https://restcountries.com/v2/name/${c2}`);
    // const [data3] = await getJSON(`https://restcountries.com/v2/name/${c3}`);

    //병렬적으로 다운로드 받기! Promis.all([promise1, promise2, promise3])
    const data = await Promise.all([
      getJSON(`https://restcountries.com/v2/name/${c1}`),
      getJSON(`https://restcountries.com/v2/name/${c2}`),
      getJSON(`https://restcountries.com/v2/name/${c3}`),
    ]);

    console.log(data.map(d => d[0].capital));
  } catch (err) {
    console.error(err);
  }
};
get3Countries('portugal', 'usa', 'india');

Promise.race

array of promises를 받고 가장빨리 settled된 promise를 return 한다.

(async function () {
  const res = await Promise.race([
    getJSON(`https://restcountries.com/v2/name/italy`),
    getJSON(`https://restcountries.com/v2/name/egypt`),
    getJSON(`https://restcountries.com/v2/name/portugal`),
  ]);
  console.log(res[0]); //italy
})();

const timeout = function (sec) {
  return new Promise(function (_, reject) {
    setTimeout(() => {
      reject(new Error('Request took too long!'));
    }, sec * 1000);
  });
};

Promise.race([getJSON(`https://restcountries.com/v2/name/italy`), timeout(1)])
  .then(res => console.log(res[0]))
  .catch(err => console.error(err));

Promise.allSettled

모든 promise의 result들을 return 한다. (reject 경우도 포함해서)

Promise.allSettled([
  Promise.resolve('Success'),
  Promise.reject('ERROR'),
  Promise.resolve('Another success'),
])
  .then(res => console.log(res))
  .catch(err => console.errror(err));

promise.any [ES2021]

first fulfilled promise를 return한다. reject promise는 무시한다.

Promise.any([
  Promise.resolve('Success'),
  Promise.reject('ERROR'),
  Promise.resolve('Another success'),
])
  .then(res => console.log(res))
  .catch(err => console.errror(err));
// Success

Coding challenge

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

const imagesContainer = document.querySelector('.images');
const createImage = function (imgPath) {
  return new Promise(function (resolve, reject) {
    const img = document.createElement('img');
    img.src = imgPath;
    img.addEventListener('load', () => {
      imagesContainer.appendChild(img);
      resolve(img);
    });
    img.addEventListener('error', err => {
      reject(new Error('Image not found'));
    });
  });
};

createImage('img/img-1.jpg')
 .then(img => {
   currentImage = img;
   return wait(2);
 })
 .then(() => {
   currentImage.style.display = 'none';
   return createImage('img/img-2.jpg');
 })
 .then(img => {
   currentImage = img;
   return wait(2);
 })
 .then(() => (currentImage.style.display = 'none'))
 .catch(err => console.error(err));

Part 1
위의 promise를 async function으로 만들기

const loadNPause = async function () {
  try {
    // load image1
    let img = await createImage('img/img-1.jpg');
    await wait(2);
    img.style.display = 'none';

    // load image2
    img = await createImage('img/img-2.jpg');
    await wait(2);
    img.style.display = 'none';
  } catch (err) {
    console.error(err);
  }
};

//loadNPause();

Part 2
유의할 포인트! async 함수는 항상 promise를 return한다!
fullfilled value를 얻고 싶다면 await 뒤에 적은 뒤 변수로 묶어줄 것

ex) const imgs = imgArr.map(async img => await console.log(img)) => promise 배열

const loadAll = async function (imgArr) {
  try {
    const imgs = imgArr.map(async img => await createImage(img));
    const imgsEl = await Promise.all(imgs);
    imgsEl.forEach(img => img.classList.add('parallel'));
  } catch (err) {
    console.error(err);
  }
};

loadAll(['img/img-1.jpg', 'img/img-2.jpg', 'img/img-3.jpg']);

좋은 웹페이지 즐겨찾기