// async & await 은 promise 를 좀 더 간결하고 간편하고 또, 동기적으로 실행되는 것처럼 보이게 만들어준다.
// 새로운 거라기 보단 promise 위에 좀 더 간편한 api 를 얹은 것이다.
// 이러한 것을 syntatic sugar 이라고 한다. (class 또한 sugar 이다.)

// 1. async
// function  fetchUser() {
//     // do network request in 10 secs...
//     return 'ellie';    
// }

// const user = fetchUser(); // 10 초동안 다른 작업을 하지 못한다.
// console.log(user); //  10초가 지난 후에 출력이 되는 것이다.
// //어떻게 해야 했었지?

function  fetchUser() {
    return new Promise((resolve, reject) => {
    // do network request in 10 secs...    
    //return 'ellie';
    resolve('ellie');
    });
}

const user = fetchUser();
user.then((user) => console.log(user)); //이거를 간단하게 바꾸면
user.then(console.log);

//console.log(user); // resolve 와 reject 를 호출하지 않고 return 시키면 Promise {<pending>} 이 뜬다.
// pending 상태인 것이다. 

// 위의 return 'ellie' 를 ---> resolve('ellie') 로 바꿔주면 Promise {<fulfilled>: "ellie"} 이 뜬다.

console.clear();
// 1. async. function 앞에 async 를 붙여주면 항상 promise 를 반환한다.
async function  fetchUser2() {   
    // do network request in 10 secs...    
    return 'ellie';
}

const user2 = fetchUser2();
user.then(console.log); // promise 안써도 되는 것 확인. 자동적으로 함수의 코드 블럭이 promise 로 변한된다.

// 2. await
// async 붙은 func 안에서만 사용가능하다. 

function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function getApple() {
    await delay(1000); //await 뒤에는 promise 가 온다. 그리고 promise 가 처리 될 때까지 기다린다.
    return '🍎';
}

async function getBanana() {
    await delay(1000);
    return '🍌';
}

// function getBanana(params) {
//     return delay(3000)
//     .then(()=> '🍔');
// }

//callback 지옥 ㅠㅠ
// function pickFruits() {
//     return getApple()
//     .then(apple => {
//         return getBanana()
//         .then(banana => `${apple} + ${banana}`);
//     });
// }

// async 를 이용해서 간단하게 바꿔보자!!!
async function pickFruits() {
		// const apple = await getApple();
    // const banana = await getBanana(); // getApple 1초 기다리고, getBanana 1초 기다리고... 비효율적이다.
    // 둘은 서로 연관되 있지 않기 때문에 기다릴 필요가 없다.
    const applePromise = getApple(); // promise 는 만드는 순간 바로 실행
    const bananaPromise = getBanana();    
    const apple = await applePromise;
    const banana = await bananaPromise; // 병렬적으로 실행되서 1초만에 된다.
    return `${apple} + ${banana}`;
}
//그렇지만 위와 같이 지저분하게 하지 않는다.

pickFruits().then(fr => console.log(fr));

// 3. useful Promise APIS
function pickAllFruits() {
    return Promise.all([getApple(), getBanana()]) // 병렬적으로 다 받아 준다.
    .then(fruits => fruits.join(' + '));
}

pickFruits().then(console.log); // 🍎 + 🍌

function pickOnlyOne() {
    return Promise.race([getApple(), getBanana()]); // 가장 먼저 return 받는 애를 출력한다.
}

pickOnlyOne().then(console.log); // 🍎
const f1 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("1번 주문 완료");
      //reject(new Error("1번 error!"));
    }, 1000);
  });
};

const f2 = (message) => {
  console.log(message);
  return new Promise((res, rej) => {
    setTimeout(() => {
      res("2번 주문 완료");
      //rej(new Error("Error!"));
    }, 2000);
  });
};

const f3 = (message) => {
  console.log(message);
  return new Promise((res, rej) => {
    setTimeout(() => {
      res("3번 주문 완료");
    }, 3000);
  });
};

async function order() {
  const result1 = await f1();
  const result2 = await f2(result1);
  const result3 = await f3(result2);
  console.log(result3);
}
// 앞장 Promise 에서의 코드와 비교했을 때 직관적으로 바뀌었다. 단! 1초, 2초 3초 해서 6초 걸린다.

//error 가 발생하면 어떻게? promise.then 으로 처리할 때는 catch() 로 받았다. 여기선 try-catch 로 핸들링한다.
async function order() {
  try {
    const result1 = await f1();
    const result2 = await f2(result1);
    const result3 = await f3(result2);
    console.log(result3);
  } catch (e) {
    console.log(e);
  }
}
==================================================================================================
async function order() {
  try {
    const result = await Promise.all([f1(), f2(), f3()]); // async await 함수에서도 비동기 함수를 병렬로 처리 가능하다. 3초 걸린다.
    console.log(result); // ["1번 주문 완료", "2번 주문 완료", "3번 주문 완료"]
  } catch (e) {
    console.log(e);
  }
  console.log("종료");
}