티스토리 뷰

자바스크립트에서 function(함수)는 object(객체)이다

이름이 없는 함수를 익명함수라고 한다


함수 실행시 조건이 맞지 않으면 바로 종료하도록 빠르게 return을 넣어 짜는 것을 추천

// 비추천
function A (score) {
  if (score > 10) {
  // 길고 긴 코드
  }
};


// 추천
function A (score) {
  if (score <= 10) {
  return;
  }
  // 길고 긴 코드
};

자바스크립트는 synchronus(동기적)

> 호이스팅이 된 이후부터 작성한 순서대로 실행됨

 

하지만 asynchronus(비동기적)인 함수도 있음

> 코드가 언제 실행될지 알 수 없음

예) setTimeout();

 

비동기의 값은 실패, 성공만 가져옴

내가 요청한 value를 가져오지 않음, value는 콜백함수가 받음


콜백함수 callback function

함수에서 다른 함수를 인자로 전달해서 호출하는 것

function randomQuiz(answer, printYes, printNo) {
  if (answer === 'yes') {
    printYes();
  } else {
    printNo();
  }
}

const printYes = function () {
  console.log('yes');
}

const printNo = function () {
  console.log('No');
}

동기적 콜백 / 비동기적 콜백

// Synchronous callback
function printImmediately(print) {
print();
}
printImmediately(() => console.log('hello'));

// Asynchronous callback
function printWithDelay(print, timeout) {
  setTimeout(print, timeout);
}
printWithDelay(() => console.log('async callback'), 2000);

콜백지옥

함수에서 콜백함수를 호출하고 전달하고 또 그안에서 호출하고 전달하고 하는 것

!문제점

- 가독성이 떨어짐

- 로직을 한눈에 알아보기가 어려움

- 에러가 발생해서 디버깅 해야 할 때도 어려움

- 유지 보수도 어려움

// Callback Hell example
class UserStorage {
  loginUser(id, password, onSuccess, onError) {
    setTimeout(()=>{
      if (
        (id === 'user1' && password === 'password1') ||
        (id === 'user2' && password === 'password2')
      ) {
        onSuccess(id);
      } else {
        onError(new Error('not found'));
      }
    }, 2000);
  }

  getRoles(user, onSuccess, onError) {
    setTimeout(()=>{
      if (user === 'user3') {
        onSuccess({name: 'bong', role: 'admin'});
      } else {
        onError(new Error('no access'));
      }
    }, 1000);
  }
}

const UserStorage = new UserStorage();
const id = prompt('enter your id');
const password = prompt('enter your password');
UserStorage.loginUser(
  id, 
  password, 
  user => {
    UserStorage.getRoles (
      user,
      userWithRole => {
        alert(`Hello ${userWithRole.name}, you have a ${userWithRole.role} role`);
      },
      error => {
        console.log(error);
      }
    );
  }, 
  error => {
    console.log(error);
  }
)

promise

자바스크립트에 내장된 비동기를 간편하게 처리할 수 있도록 도와주는 오브젝트

콜백함수 대신 유용하게 사용할 수 있음

정해진 기능을 수행하고 정상적으로 작동했다면 성공메세지와 결과값 전달

기능을 수행하다가 문제가 발생하면 에러를 전달

 

promise 생성

// 1. Producer
const promise = new Promise((resolve, reject) => {
  // doing some heavy work ()
});

무거운 작업을 수행할 때 동기적으로 처리하면 작업을 진행하는 동안 코드가 실행이 되지 않으므로

시간이 오래 걸리는 작업은 비동기적으로 처리하는 것이 좋음

예) 네트워크, 파일 읽어오기

 

promise 안에서 꼭 resolve나 reject로 끝내줘야함

그렇지 않으면 계속 pending 상태로 있음

 

promise는 생성되는 그 순간 인자로 받은 콜백함수가 실행되므로 주의!

특정행동을 하면 콜백함수가 실행되어야 할 때는 불필요한 작업이 실행될 수 있음!

 

promise 사용

then이 있으면 콜백 함수가 실행완료가 되는데로 보여줄게! (비동기적 처리)

// 1. Producer
const promise = new Promise((resolve, reject) => {
  // doing some heavy work ()
  setTimeout(() => {
    // resolve('bong');
    reject(new Error('no network')); // new Error : 자바스크립트에서 제공하는 객체
  }, 2000);
});

// 2. Consumers: then, catch, finally
promise
  .then((value) => { // 성공했을 때 호출
    console.log(value);
  })
  .catch((error) => { // 실패했을 때 호출
    console.log(error);
  })
  .finally(() => { // 성공하던 실패하던 마지막으로 실행하고 싶을 때 사용
    console.log('finally');
  });

Promise chaining

// 3. Promise chaining
const fetchNumber = new Promise((resolve, reject) => {
  setTimeout(() => resolve(1), 1000);
});

fetchNumber
.then(num => num * 2) // 2
.then(num => num * 3) // 6
.then(num => {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(num - 1), 1000); // 5
  });
})
.then(num => console.log(num)); // 5

then은 값을 바로 전달 할 수도 있고 Promise를 전달 할 수도 있음


Error Handling

성공적으로 작동되었을 때

// 4. Error Handling
const getHen = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve('🐓'), 1000);
  });
const getEgg = hen =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve(`${hen} => 🥚`), 1000);
  });
const cook = egg =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve(`${egg} => 🍳`), 1000);
  });

  getHen()
  .then(hen => getEgg(hen))
  .then(egg => cook(egg))
  .then(meal => console.log(meal));

이렇게 코드를 줄일 수도 있음

  getHen()
  .then(getEgg) // 한가지만 받아서 그대로 전달하는 경우 생략 가능
  .then(cook)
  .then(console.log);

오류가 났을 때!

const getHen = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve('🐓'), 1000);
  });
const getEgg = hen =>
  new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error(`error! ${hen} => 🥚`)), 1000);
  });
const cook = egg =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve(`${egg} => 🍳`), 1000);
  });

  getHen()
  .then(getEgg)
  .catch(error => { // 받아오면서 오류가 났을 때 체인 전체가 실패하지 않도록 에러를 처리해주기
    return '🥖';
  })
  .then(cook)
  .then(console.log)
  .catch(console.log);

중간에 오류가 날 경우를 대비하여 에러를 처리해주는게 좋음

처리해주면 return된 값을 넣어서 결과가 출력되지만

처리하지 않으면 출력된 결과는 에러가 나옴

 

위의 콜백지옥을 promise를 사용하여 더 좋게 바꿀 수 있음

class UserStorage {
  loginUser(id, password) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (
          (id === 'ellie' && password === 'dream') ||
          (id === 'coder' && password === 'academy')
        ) {
          resolve(id);
        } else {
          reject(new Error('not found'));
        }
      }, 2000);
    });
  }

  getRoles(user) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (user === 'ellie') {
          resolve({ name: 'ellie', role: 'admin' });
        } else {
          reject(new Error('no access'));
        }
      }, 1000);
    });
  }
}

const userStorage = new UserStorage();
const id = prompt('enter your id');
const password = prompt('enter your password');
userStorage
  .loginUser(id, password)
  .then(userStorage.getRoles)
  .then(user => alert(`Hello ${user.name}, you have a ${user.role} role`))
  .catch(console.log);

async / await

기존의 promise 위에 좀 더 API가 추가된 것 (syntactic sugar)

그렇다고 무조건 promise보다 async / await 이 좋은 것은 아님! 경우에 따라 더 적합한 것을 사용하면 됨

 

async 사용

함수 앞에 async를 적으면 promise로 바로 만들 수 있음

// 1. async
async function fetchUser() { // 자동으로 promise로 바뀜
  // 10초 정도 걸리는 네트워크
  return 'user name';
}

const user = fetchUser();
user.then(console.log);
console.log(user);

 

await 사용

async가 사용된 함수 안에서만 사용 가능

await이 적힌 함수가 끝날 때까지 기다리도록 함 > 동기적으로 적은 코드처럼 가독성이 더 높아짐

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

async function getApple() {
  await delay(3000); // delay함수가 끝날 때 까지 기다려라
  return '🍎';
}

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

promise도 너무 중첩하면 콜백지옥처럼 됨

function pickFruits() {
  return getApple()
  .then(apple => {
    return getBanana()
    .then(getBanana => `${apple} 와(과) ${banana}`);
  });
}

pickFruits().then(console.log);

 

async를 사용해서 간단히 써 줄 수 있음

동기적으로 적은 코드처럼 적어줄 수 있음

async function pickFruits() {
  const apple = await getApple();
  const banana = await getBanana();
  return `${apple} 와(과) ${banana}`;
}

pickFruits().then(console.log);

await 동기화

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

async function getApple() {
  await delay(3000);
  return '🍎';
}

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

async function pickFruits() {
  const apple = await getApple(); // 3초
  const banana = await getBanana(); // 3초
  return `${apple} 와(과) ${banana}`; // 6초나 걸림
}

pickFruits().then(console.log);

이렇게 적으면 await 때문에 차례대로 실행하기 때문에 return을 하는데까지 6초가 걸림

> 값을 따로따로 받아오는데 굳이 기다리는 것은 비효율적

 

async function pickFruits() {
  const applePromise = getApple(); // apple promise 생성
  const bananaPromise = getBanana(); // banana promise 생성
  const apple = await applePromise(); // 동기화
  const banana = await bananaPromise(); // 동기화
  return `${apple} 와(과) ${banana}`; // 3초가 걸림
}

pickFruits().then(console.log);

promise는 생성되는 순간 내장 함수를 바로 실행하기 때문에 promise와 await을 동기화를 해줘서

굳이 기다릴 필요 없이 사과와 바나나를 한번에 출력할 수 있음

하지만 값을 받아오는데 연관이 없는 병렬적인 promise는 저렇게 길게 쓸 필요 없음

 

값을 받아오는데 연관이 없이 병렬적일 때는 Promise.all 사용

function promiseAll() {
  return Promise.all([Promise1(), Promise2()]) // 받아온 배열의 Promise들을 모두 병렬적으로 실행
  .then(() => {}) // 받아진 배열을 전달, 모두 받아온 뒤에 실행
}
function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function getApple() {
  await delay(3000);
  return '🍎';
}

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

function pickAllFruits() {
  return Promise.all([getApple(), getBanana()])
  .then(fruits => fruits.join('와(과)'));
}

pickAllFruits().then(console.log); // 사과와 바나나 동시에 출력

 

제일 먼저 끝나는 Promise 하나만 받고 싶다!

Promise.race 사용

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

async function getApple() {
  await delay(3000);
  return '🍎';
}

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

function pickOnlyOne() {
  return Promise.race([getApple(), getBanana()]);
}

pickOnlyOne().then(console.log); // 바나나 출력

async 에러 처리

throw

async function getApple() {
  await delay(3000);
  throw 'error'; // 이렇게하면 에러가 발생
  return '🍎';
}

try / catch

async function pickFruits() {
  try {
    const apple = await getApple();
    const banana = await getBanana();
    return `${apple} 와(과) ${banana}`;
  } catch() {
    // 에러 처리
  }
}
728x90

'공부 노트' 카테고리의 다른 글

화살표함수 arrow function  (0) 2021.12.11
map(), filter()  (0) 2021.12.11
재귀 함수  (0) 2021.12.06
시맨틱 태그(Semantic tag)  (0) 2021.09.27
메뉴를 만들 때 <li>를 사용하는 이유  (0) 2021.09.23
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함