ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 비동기적 방식 처리 방법 (Callback, Promise, async &await)
    JavaScript 2022. 2. 11. 13:36

    비동기적 방식 처리 방법

    Callback 함수
    Promises
    async & await

     


    Callback 함수

    Callback 이란?

    • 다른 함수가 실행을 끝낸 뒤 실행(call back)되는 함수(⇒ 나중에 호출되는 함수)를 말한다.
    • 다시 말해 코드를 통해 명시적으로 호출하는 함수가 아니라, 함수를 등록해놓은 후 어떤 이벤트가 발생했거나 특정 시점에 도달했을 때 시스템에서 호출하는 함수를 말한다.
    • 파라미터로 함수를 전달받아, 함수의 내부에서 실행된다.

     

    콜백함수 (Callback Function) 사용 이유

    • 자바스크립트에서 비동기적 프로그래밍을 할 수 있기 때문이다.
    • 자바스크립트는 싱글스레드를 사용하는데, 멈춤을 방지해준다. 즉 블록킹을 방지하여 싱글스레드가 논블록킹으로 동작하게 한다.
    💡  싱글스레드
          ⇒ 싱글스레드는 한 번에 하나의 작업만 수행할 수 있다.

    💡  싱글스레드로 어떻게 비동기 작업이 가능할까?
    • 자바스크립트의 메인스레드인 이벤트 루프가 싱글스레드이기 때문에 자바스크립트를 싱글스레드 언어라고 부른다.
      그러나 자바스크립트는 이벤트 루프만 독립적으로 실행하지는 않고, 웹 브라우저나 NodeJS 같은 멀티 쓰레드 환경에서 실행된다.
    • 즉, 동시성을 보장하는 비동기, 논블록킹 작업들은 JavaScript 엔진을 구동하는 웹 브라우저, NodeJS (런타임 환경) 에서 담당한다.
      JavaScript 동작원리에서 자세하게 알 수 있다.

     

    콜백함수(Callback Function) 사용 유형

    1. 익명 함수 사용
      • 콜백함수는 이름 없는 '익명의 함수'를 사용한다. 함수의 내부에서 실행되기 때문에 이름을 붙이지 않아도 된다.
    2. 함수의 이름(만) 넘기기
      • 자바스크립트는 null 과 undefined 타입을 제외하고 모든 것을 객체로 다룬다. 함수를 변수 또는 다른 함수의 변수처럼 사용할 수 있다. 함수를 콜백함수로 사용할 경우, 함수의 이름만 넘겨주면 된다.
    3. 전역변수, 지역변수를 콜백함수의 파라미터로 전달 가능
      • 전역변수(Global Variable) : 함수 외부에서 선언된 변수
      • 지역변수 (Local Variable) : 함수 내부에서 선언된 변수

     

    콜백함수(Callback Function) 주의할 점

    1. this를 사용한 콜백함수
    2. 콜백지옥 (Callback Hell)

    • 콜백 지옥(Callback Hell)은 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 감당하기 힘들정도로 깊어지는 현상이다.
    • 주로 이벤트 처리나 서버 통신과 같은 비동기적인 작업을 수행하기 위해 이런 형태가 자주 등장하는데, 가독성이 떨어지면서 코드를 수정하기 어려워진다.
    • 비동기적인 작업을 수행하기 위해 콜백함수를 익명함수로 전달하는 과정에서 생기는 콜백 지옥을
    • Promise, async/await, Generator 등을 사용해 방지할 수 있다.

     

     

     


    Promise

    Promise 란?

    • 프로미스는 자바스크립트 비동기 처리에 사용되는 객체이다.
    • 싱글스레드인 자바스크립트에서 비동기 처리를 위해 사용한 Callback 함수의 에러/예외처리의 어려움, 중첩으로 인한 복잡도 증가라는 단점을 해결하기 위해 프로미스 객체를 ES6에서 언어적 차원으로 지원한다. 
    💡   Promise가 콜백을 대체하는 것은 아니지만, 콜백을 예측 가능한 패턴으로 사용할 수 있게 하며 Promise 없이 콜백만 사용했을 때 예상치         못한 동작을 막아주거나 찾기 힘든 버그를 상당 수 해결해준다.

     

    Promise 만들기

    const promise = new Promise((resolve, reject) => {
      /*
      비동기 작업 성공시 resolve()를 호출하고,
      비동기 작업 실패시 reject()를 호출하도록 구현한다.
      */
    })
    
    • Promise 성공 시 resolve를 호출하고 실패 시 reject를 호출한다.

     

    const promise = new Promise((resolve, reject)=>{
    	//처리 내용
    })
    
    promise.then(
    	//resolve가 호출되면 then이 실행
    )
    .catch(
    	//reject가 호출되면 catch가 실행
    )
    .finally(
    	//콜백 작업을 마치고 무조건 실행되는 finally (생략 가능)
    )
    
    • Promise 다음엔 then()catch()를 사용한다.
    • then()은 생성한 프로미스 객체에서 인수로 전달한 resolve 가 호출되면 실행된다.
    • catch()는 생성한 프로미스 객체에서 인수로 전달한 resolve 가 호출되면 실행된다.

     

    // Promise 예제
    
    const flag = true;
    const promise = new Promise(((resolve, reject) => {
        if (flag) {
            resolve('resolve가 되었음')
        }
        else {
            reject('reject가 되었음')
        }
    }))
    
    promise.then((resolveMessage) => {
        console.log(resolveMessage)
    })
    .catch((errorMessage) => {
    	console.log(errorMessage)
    })
    
    // 결과 : resolve가 되었음
    
    • flag 가 true 면 resolve 를 호출하고 false 면 reject 를 호출한다.
    • resolve와 reject는 각각 호출되어 인수로 문자열을 전달한다.

     

    Promise 객체로 비동기 처리 연결하기 (Promise Chaining)

    • then()과 catch() 뒤에는 또다른 then()과 catch()를 연결함으로써 비동기 처리를 연결할 수 있다.
    // Promise 예제
    
    const flag = true
    const promise = new Promise(((resolve, reject) => {
        if (flag) {
            resolve('resolve가 되었음')
        }
        else {
            reject('reject가 되었음')
        }
    }))
    
    promise.then((resolveMessage) => {
        console.log(resolveMessage)
    
        return new Promise(((resolve, reject) => {
            if (flag) {
                resolve('resolve가 되었음2')
            }
            else {
                reject('reject가 되었음2')
            }
        }))
    })
    .then((resolveMessage2)=> {
        console.log(resolveMessage2)
    })
    .catch((errorMessage) => {
        console.log(errorMessage)
    })
    
    // 결과 : resolve가 되었음
    //			 resolve가 되었음2
    

     

    Promise 의 3가지 상태 (states)

    • 프로미스의 상태(states)란 프로미스의 처리 과정을 의미한다.
    • new Promise() 로 프로미스를 생성하고 종료될 때까지 3가지 상태를 갖는다.
      1. Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
        • // new Promise() 메서드를 호출하면 대기 상태가 된다.
          new Promise()
          
          // new Promise() 호출 시 콜백 함수를 선언할 수 있고, 인자는 resolve, reject 이다.
          new Promise(function(resolve, reject) {
          	//...
          })
          
      2. Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
        • // new Promise() 메서드를 호출하면 대기 상태가 된다.
          new Promise()
          
          // new Promise() 호출 시 콜백 함수를 선언할 수 있고, 인자는 resolve, reject 이다.
          new Promise(function(resolve, reject) {
          	//...
          })
          
      3. Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태
        • // 콜백 함수의 인자 reject를 실행하면 실패(Rejected) 상태가 된다.
          new Promise(function(resolve, reject) {
          	reject()
          })
          
          // 실패 상태가 되면 실패한 이유 (실패 처리의 결과 값)를 catch()로 받을 수 있다.
          function getData() {
          	return new Promise(function(resolve, reject) {
          		reject(new Error("Request is failed"))
          	})
          }
          getData().then().catch(function(err) {
          	console.log(err) // 결과 : Error: Request is failed
          })
          
    • 완료 상태인 Fulfilled 와 Rejected 를 합쳐 settled 라고 한다.

     

    Promise 처리의 흐름

     

    Promise의 에러 처리 방법

    • 실제 서비스를 구현하다 보면 네트워크 연결, 서버 문제 등으로 인해 오류가 발생할 수도 있다.
    • 에러 처리 방법은 2가지가 있는데, 모두 프로미스의 reject() 메서드가 호출되어 실패 상태가 된 경우에 실행된다.
      1. then() 의 두 번째 인자로 에러를 처리하는 방법
        • getData().then(
            handleSuccess,
            handleError
          )
          
      2. catch() 를 이용하는 방법
        • getData().then().catch();
          
        • ⇒ 프로미스의 에러 처리는 가급적 catch()를 이용하는 것이 효율적이다.

     

     


    async & await

    async & await 란?

    • async & await 는 비동기식 코드를 동기식으로 표현하여 간단하게 나타내는 것 이다.
    • 기존의 비동기 처리 방식인 Callback 함수의 단점을 보완하기 위해 Promise 를 사용했지만, 코드가 장황하다는 단점이 있었다.
    • 이러한 단점을 해결하기 위해 ES2017(ES8)에서 도입된 비동기 처리 방식의 가장 최신 문법이다.
    • async & await 는 Promise 객체를 반환한다. ( ⇒ then을 사용할 수 있다.)
      • async function myAsync(){
        	return 'async';
        }
        
        myAsync().then((result) => {
        	console.log(result); // 결과 : async
        })
        
        • 프로미스를 반환하고 then()으로 받게 되는 인자 result는 바로 return 값을 의미한다.

     

    async & await 기본 문법

    async function 함수명() {
      await 비동기_처리_메서드_명()
    }
    
    • 함수의 앞에 async 라는 예약어를 붙인다.
    • 함수의 내부 로직 중 HTTP 통신을 하는 비동기 처리 코드 앞에 await 를 붙인다.
      • ⇒ 비동기 처리 메서드가 꼭 프로미스 객체를 반환해야 await 가 의도한 대로 동작한다.
      • (일반적으로 await 의 대상이 되는 비동기 처리 코드는 Axios 등 프로미스를 반환하는 API 호출 함수이다.)

     

    1. async 함수
      • async 는 function 앞에 위치한다.
      • funciton 앞에 async 를 붙이면 해당 함수는 항상 프로미스를 반환한다.
        • 프로미스가 아닌 값을 반환하더라도 이행 상태의 프로미스(resolved promise)로 값을 감싸 이행된 프로미스가 반환되도록 한다.
        • ⇒ async 가 붙은 함수는 반드시 프로미스를 반환하고, 프로미스가 아닌 것은 프로미스로 감싸 반환한다.
      • async 함수는 화살표 함수로도 정의가 가능하고, 함수 표현식으로도 정의가 가능하다.
    2. await
      • awaitasync 함수 안에서만 동작한다.
      • 자바스크립트는 await 키워드를 만나면 프로미스가 처리(settled)될 때까지 기다리고, 결과는 그 이후 반환된다.
    // async & await 예제
    
    function delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
      }
    
      //2초동안 기다리게 하고 사과를 리턴하는 메서드
      async function getApple() {
        await delay(2000)
        return '🍎'
      }
    
      //1초동안 기다라게 하고 사과를 리턴하는 메서드
      async function getBanana() {
        await delay(1000)
        return '🍌'
      
    getApple().then(console.log) // 결과 : 🍎
    getBanana().then(console.log) // 결과 : 🍌
    

     

    async & await 예외 처리

    • async & await 에서 예외를 처리하는 방법은 try.. catch.. 구문을 사용하는 것이다.
    • catch {} 를 사용하면 된다. (Promise 에서는 .catch()사용)
    function fetchData() {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                return resolve('success')
            }, 1000)
        })
    }
    
    async function loadData() {
        try {
            const result = await fetchData()
            console.log(result)
        } catch(e) {
            console.log(e)    
    		}
    }
    
    loadData()
    

     

    반응형

    'JavaScript' 카테고리의 다른 글

    페이지 처리  (0) 2022.02.11
    이벤트 핸들러 (EventHandler)  (0) 2022.02.11
    비동기 프로그래밍  (0) 2022.02.11
    JavaScript 동작원리  (0) 2022.02.07
    JavaScript  (0) 2022.02.07

    댓글

Designed by Tistory.