[TIL 27] 비동기

yezo cha·2021년 6월 22일
0

JavaScript

목록 보기
17/19
post-thumbnail
post-custom-banner

비동기 처리가 뭘까?

자바스크립트의 비동기 처리특정 코드의 연산이 끝날 때까지 기다려주지 않고 나머지 코드를 먼저 실행하는 것을 의미한다.

JavaScript is synchronous.
Execute the code block by orger after hoisting.

아래 코드를 콘솔에서 확인하면 동기, 비동기 이해될 듯..

console.log('1')
setTimeout(() => console.log('2'), 1000);
console.log('3');

// 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);

setTimeOut()

setTimeOut()은 Web API의 한 종류이다.
코드를 바로 실행하지 않고 지정한 시간만큼 기다렸다가 로직을 실행한다.

비동기 처리가 필요한 이유 ???

화면에서 서버로 데이터를 요청했을 때, 서버가 언제 그 요청에 대한 응답을 줄 지 모르는데 마냥 다른 코드를 실행하지 않고 기다릴 수 없기 때문 ! 너무 오래 걸려..

Callback

JavaScript: What the heck is a Callback?
콜백다른 함수가 실행을 끝낸 뒤 실행되는 — call back 되는 함수를 말한다.

자바스크립트에서 함수object이다.
이 때문에 함수는 다른 함수의 인자로 쓰일 수도, 어떤 함수에 의해 리턴될 수도 있다. 이러한 함수를 고차 함수 higher-order functions 라 부르고 인자로 넘겨지는 함수콜백 함수 callback function 라고 부른다.

콜백 지옥 Callback Hell

콜백 지옥은 비동기 처리 로직을 위해 콜백 함수를 연속해서 사용할 때 발생하는 문제이다.

  • 콜백 체인 문제점
    • 가독성이 떨어진다.
    • 로직을 이해하기 힘들다.
    • 디버깅 할 때, 어렵다.
    • 유지보수 힘들다.

Promise

"A promise is an object that may produce a single value some time in the future"

프로미스는 자바스크립트 비동기 처리 에 사용되는 객체다.
주로 서버에서 받아온 데이터를 화면에 표시할 때 사용한다.

프로미스 객체는 아래와 같은 문법으로 만들 수 있다.

Producer

const promise = new Promise((resolve, reject) => {
	// executor
  console.log('doing something...');
  setTimeout(() => {
    resolve('yezo..');		      // 성공하면
    reject(new Error('no network'));  // 실패하면
  }, 2000);
});

Consumer

  • then, catch, finally
promise
    .then((value) => {
        console.log(value);
    })
    .catch((error) => {
        console.log(error);
    })
    .finally(() => {    // 성공하든 실패하든 마지막에 실행됨.
        console.log('finally');
    })

Promise의 3가지 상태 (states)

State : pending(보류) -> fulfilled(이행) or rejected(거부)

  • Pending(대기)
    비동기 처리 로직이 아직 완료되지 않은 상태.
  • Fulfilled(이행)
    비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태.
  • Rejected(실패)
    비동기 처리가 실패하거나 오류가 발생한 상태.

Promise Chaining

여러 개의 프로미스를 연결하여 사용할 수 있다.
then() 메서드를 호출하고 나면 새로운 프로미스 객체가 반환된다.

new Promise((resolve, reject) => {
    setTimeout(() => resolve(1), 2000);
})
.then((result) => {
    console.log(result);
    return result + 10;	 // 1
})
.then((result) => {
    console.log(result);
    return result + 20;	 // 11
})
.then((result) => {
    console.log(result); // 31
});

프로미스 객체 하나를 생성하고 setTimeout()을 이용해 2초 후에 resolve()를 호출하는 코드가 있다.

resolve()가 호출되면 프로미스가 Pending 상태에서 Fulfilled 상태로 넘어가기 때문에 첫 번째 then()의 로직으로 넘어간다.
첫 번째 then()에서는 이행된 결과 값 1을 받아서 10을 더한 후, 그 다음 then()으로 넘겨준다.
두 번째 then()에서도 바로 이전 프로미스의 결과 값 11을 받아서 20을 더하고 다음 then()으로 넘겨준다.
마지막 then()에서 최종 결과 값 31을 출력한다.

Error Handling

1. then()의 두 번째 인자로 에러를 처리하기

2. catch()를 이용하기

function getData() {
    return new Promise((resolve, reject) => {
        reject('failed');
    });
}
// 1. then()의 두 번째 인자로 에러를 처리하는 코드
getData()
    .then(() => {
        // ...
    }, (err) => {
        console.log(err);
    });

// 2. catch()로 에러를 처리하는 코드
getData()
    .then()
    .catch((err) => console.log(err));

프로미스 에러 처리는 가급적 catch()를 사용하자 !
더 효율적이다..
더 많은 예외 처리를 위해 catch()를 붙여주자..

Async / await

async & await = clear style of using Promise.
asyncawait는 자바스크립트 비동기 처리 패턴 중에 가장 최근에 나온 문법이다.

기존의 비동기 처리 방식인 콜백 함수와 프로미스의 단점을 보완하고 좀 더 읽기 좋은 코드를 작성할 수 있게 도와준다.
프로미스 체이닝을 계속 하다보면 코드의 가독성이 떨어지게 된다.
asyncawaitPromise를 간결하고 동기적으로 실행되는 것처럼 보이게 만들어준다.

asyncawait새로운 것이 추가된 것이 아니라, 기존에 존재하는 Promise 위에 조금 더 간편한 API를 제공한다. -> syntatic sugar

Async

Promise 사용 방식

function fetchUser() {
    return new Promise((resolve, reject) => {
        return 'yezo';                  // 프로미스 state : pending
        resolve('yezo');                // 프로미스 state : fulfilled
        reject(new Error('error'));     // 프로미스 state : rejected
    });
}
const user = fetchUser();
user.then(user => console.log(user));

Async 사용

// 1. 함수 선언식
async function fetchUser() {
    return 'yezo..';
}
// 2. 함수 표현식
const fetchUser = async function() {
    return 'yezo';
} 
// 3. 화살표 함수
const fetchUser = async() => {
    return 'yezo';
}

const user = fetchUser();   			// 변수에 할당해서 호출
//fetchUser().then(data => console.log(data));    // 함수로 바로 호출
user.then(console.log);

Await

Promise 방식

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

function getApple() {
  return delay(2000)
  .then(() => '🍎');
}
function getBanana() {
  return delay(1000)
  .then(() => '🍌');
}
function pickFruits() {
    return getApple()
    .then(apple => {
      return getBanana()
      .then(banana => {
        `${apple} + ${banana}`
      });
   }
}
pickFruits().then(result => console.log(result));

Await 사용

  • error 처리 : try/catch
  • await는 async함수 내부에만 사용 가능.
function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

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

async function getBanana() {
    await delay(1000);
    return '🍌';
}
async function pickFruits() {
    let apple = null;
    try {
        apple = await getApple();
    } catch(error) {
        console.log(error);
    }

    let banana = null;
    try {
        banana = await getBanana();
    } catch(error) {
        console.log(error);
    }
    return `${apple} + ${banana}`;
}
pickFruits().then(console.log);

Await 병렬 처리

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

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

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

// 1. 무식한 코드
// 프로미스 객체는 선언 즉시 바로 실행됨.
// getApple과 getBanana는 병렬(독립적)으로 실행됨.
async function pickFruits() {
    const applePromise = getApple();
    const bananaPromise = getBanana();
  	// 동기화
    const apple = await applePromise;
    const banana = await bananaPromise;
    return `${apple} + ${banana}`;
}
pickFruits().then(console.log);

// 2. useful Promise APIs ✨
function pickAllFruits() {
    return Promise.all([getApple(), getBanana()])
        .then(fruits => {
            fruits.join(' + ');
        });
}
pickAllFruits().then(console.log);

// 비동기 처리 중 먼저 리턴하는 것만 출력
function pickOnlyOne() {
    return Promise.race([getApple(), getBanana()]);
}
pickOnlyOne().then(console.log);
profile
(ง •̀_•́)ง
post-custom-banner

0개의 댓글