Daye Blog

기술면접_JavaScript_11_13

기술면접_JavaScript_11_13


프론트엔드 개발자 입문(비동기처리) | Callback,Promise,Async,Await

Contents

  1. Callback
    1.1 sync/async
    1.2 Callback이란?
    1.3 Callback hell
  2. Promise
    2.1 Promise란?
    2.2 Promise chaining
  3. Async/Await
    3.1 Async/Await이란?

:bell: Callback

1.1 sync/async

synchronous

  • 한 개의 작업이 실행되는 동안 다른 작업은 멈춘 상태로 유지하고 차례를 기다리는 것
  • JavaScript는 동기언어로 hoising이 된 이후 순차적으로 코드를 실행
    • hoisting: var, function declaration이 자동적으로 제일 상단에 위치하게 되는 것
console.log("1st");
console.log("2nd");
console.log("3rd");

//출력결과: 1st,2nd,3rd


asynchronous

  • 다수의 작업이 동시에 진행되는 것
  • 코드의 실행순서를 예측할 수 없음
console.log("1st");
setTimeout(()=>{
  console.log("2nd");
  },1000)
console.log("3rd");

//출력결과: 1st,3rd,2nd
//위 코드는 동기언이인 JavaScript를 비동기적으로 처리한 것


1.2 Callback이란?

다른 함수에 매개변수로 넘겨준 함수로, 넘겨받은 함수는 때가 되면 나중에 호출

//setTimeout은 콜백함수로 실행되며 지금 당장 실행되지 않고 필요한 때에 callback(호출)하여 사용
setTimeout(function(){
  console.log('1초 후 실행되는 코드')
}, 1000) 

//콜백함수는 anonymous로 Arrow function을 통해 표기가능
setTimeout(() => console.log('1초 후 실행되는 코드'), 1000)
  • Sync callback : 즉각적으로 실행되는 콜백함수
  • Async callback : 실행시간을 예측할 수 없는 콜백함수
    (ex.수강인원이 모두 채워진 경우, 강의를 오픈하고 수강생에게 관련링크를 전달)

1.3 Callback hell

정의 : 콜백함수를 반복사용하여 코드의 들여쓰기 수준이 감당하기 힘들 정도로 깊어지는 현상. 여기서 반복사용은 하나의 콜백함수 안에서 계속해서 콜백을 반복하는 것을 말함.

문제 : 코드의 가독성이 떨어지며 오류 디버깅이 어려움

Callback hell example
class UserStorage {
  //사용자 데이터를 서버에서 받아오는 코드
  loginUser(id, password, onSucess, onError) {
    setTimeout(() => {
      if (
        (id === 'ellie' && password == 'dream') ||
        (id === 'coder' && password == 'academy')
      ) {
        onSucess(id);
      } else {
        onError(new Erorr('not found'));
      }
    }, 2000);
  }

  //사용자의 역할(ex.손님,정회원)을 서버에서 받아오는 코드
  getRoles(user, onSucess, onError){
    setTimeout(() => {
      if (user === 'ellie') {
        onSucess({ name: 'ellie', role: 'admin' });
      } else {
        onError(new Error('no access'));
      }
    }, 1000);
  }
}

  /*---------------------------------------------------------*/

  const userStorage = new UserStorage(); //userStorage class delaration
  const id = prompt('enter your id');
  const password = prompt('enter your password');

  //userStorage 사용하여 로그인
  userStorage.loginUser( 
    id, 
    password, 
    user => { //1st Callback
      userStorage.getRoles(
        user,
        userWithRole => { //2nd Callback
          alert(
            `Hello ${userWithRole.name}, you have a ${userWithRole.role} role`
          );
        },
        error => { //3rd Callback
          console.log(error);
        }
      );
    },
    error => { //4th Callback
      console.log(error);
    }
);

:bell: Promise

2.1 Promise란?

  • JavaScript에 내장되어 있는 오브젝트로 비동기를 간편하게 진행할 수 있도록 함
  • 정해진 기능을 수행 후, 정상적으로 기능이 수행되었는지 혹은 오류가 발생하였는지에 대한 내용 전달
  • Promise는 생성된 순간, 즉각적으로 실행됨

Promise State
프로세스가 기능을 수행중인지 혹은 수행을 성공(실패)하였는지에 대한 상태

  1. pending : 기능 수행중
  2. fulfilled : 기능 수행성공
  3. rejected : 기능 수행실패

Producer vs Consumer
producer : 데이터 제공자 (원하는 기능을 수행하여 해당하는 데이터 생성)

const promise = new Promise((resolve, reject) => {
  // doing some heavy work(ex.서버에서 데이터를 받아오는 등의 작업을 말하며, 이를 동기적으로 실행할 경우 비효율적이므로 Promise를 사용하여 비동기적으로 실행하도록 함)
  console.log('doing something...');

  //1. Wizaed of Oz: 실제 서버를 이용하는 것과 같은 효과를 주기위해 setTimeout사용
  //2. resolve or reject사용
  serTimeout(() => {
    resolve('ellie') //기능수행에 성공할 경우 출력
  },2000);

  setTimeout(() => {
    reject(new Error('no network')) //기능수행에 실패할 경우 출력
  },2000);

});


consumer : 데이터 사용자 (원하는 데이터 소비)

  • then, catch, finally를 통해 값을 받아올 수 있음
promise
  //then: 성공할 경우, value(resolve에서 정의)에서 값을 받아와 출력
  .then((value) => {
    console.log(value);
  })

  //catch: 실패할 경우, 오류메세지 출력
  .catch(error => {
    console.log(error);
  })

  //finally: 성공,실패 여부와 무관하게 출력
  .finally(() => {
    console.log('finally');
  })

2.2 Promise chaining

Promise chaining
It allows you to chain on another then call which will run when the second promise is fulfilled

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

fetchNumber
  .then(num => num * 2)
  .then(num => console.log(num));

Promise chaining error handling

  • 다수의 Promise생성 시, reject/catch를 통해 어느 부분에서 오류가 발생했는지 확인
  • catch(error => {})를 통해 오류 대체값 설정
//Producer
const gethen = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve('hen'), 1000)
})
const getEgg = hen =>
  new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error (`error! ${hen} -> egg`)), 1000)
})
const cook = egg =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve(`${egg} -> fired egg`), 1000)
})


//Consumer
gethen()
  .then(getEgg)
  .catch(error => {
    return 'alternate';
  })
  .then(egg => cook(egg))
  .then(meal => console.log(meal))
  .catch(console.log);


Callback hell -> 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 Erorr('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) //userStorage에 id,pw전달
  .then(userStorage.getRoles) //loginUser에서 resolve일 경우, user를 받아와서 getRoles호출
  //위 코드의 경우, user= > 생략: 콜백함수 전달 시, 받아오는 value를 통해 다른함수를 바로 호출하는 경우, 생략가능
  .then(user => alert(`Hello ${user.name}, you have a ${user.role} role`))
  .catch(console.log);

3. Asnyc/Await

3.1 Asnyc/Await이란?

  • JavaScript의 비동기 처리 APIs로 Callback, Promise 대비 코드의 가독성을 높여줌(항상 Promise를 asnyc,await으로 대체해야 하는 것은 아님)
/*1. async*/
//사용자의 데이터를 받아오는 코드(동기처리): 데이터가 받아와지는 동안 다른 코드를 실행할 수 없음
function fetchUser() {
  return 'ellie'; 
}
const user = fetchUser();
console.log(user);
//사용자의 데이터를 받아오는 코드(비동기처리_async): async를 function앞에 작성하면 자동으로 블럭이 Promise로 변경됨
async function fetchUser() {
  return 'ellie'; 
}
const user = fetchUser();
console.log(user);


/*2. await*/ 
//await사용 시, delay를 통해 비동기처리를 동기처리 코드와 같이 작성할 수 있음
function delay(ms){
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function getApple() {
  await delay(1000)
  return 'apple';
}

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

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

pickFruits().then(console.log);


/*3. await의 직렬처리 문제*/ 
//아래 코드를 예시로 보았을 때, getApple과 getBanana function이 순차적으로 진행됨 (사과와 바나나는 서로 연관이 없으므로 이를 순차적으로 실행하는 것은 비효율적)
function delay(ms){
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function getApple() {
  await delay(1000)
  return 'apple';
}

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

//1번 방법(Promise사용): Promise는 생성과 동시에 실행됨으로 getApple과 getBanana를 병렬처리 할 수 있음
async function pickFruits() {
  const applePromise = getApple(); 
  const bananaPromise = getBanana(); 
  const apple = await applePromise;
  const banana = await bananaPromise;
  return `${apple} + ${banana}`
}

//2번 방법(Promise all APIs사용): Promise배열 전달 시, 모든 Promise가 병렬적으로 다 받아질 때 까지 모아줌
function pickFruits() {
  return Promise.all([getApple(), getBanana()]) //Promise가 전부 받아지면
  .then(fruits => fruits.join(' + ')); //배열의 값을 join()을 통해 string으로 병합
}

pickFruits().then(console.log);


//Appendix
function pickOnlyOne() {
  return Promise.race([getApple(), getBanana()]); //race: 배열에 전달 된 Promise중, 가장 먼저 값이 들어온 것만 return
}
기술면접_JavaScript_11_13
Prev post

기술면접_JavaScript_10

Next post

프로젝트를 Git으로 관리하는 법

기술면접_JavaScript_11_13

Get in touch

Avenco comes with a built-in contact form.