관리 메뉴

Jerry

[개념정리]Asynchronous & Promise 본문

Server/About Server

[개념정리]Asynchronous & Promise

juicyjerry 2020. 12. 21. 23:54
반응형

Achievement Goals

  • 어떤 경우에 중첩된 callback이 발생하는지 이해할 수 있다.
  • 중첩된 callback의 단점, Promise의 장점을 이해할 수 있다.
  • Promise 사용 패턴과 언어적인 특징들을 이해할 수 있다.
    • resolve, reject의 의미와, then, catch와의 관계를 이해할 수 있다.
    • Promise에서 인자를 넘기는 방법에 대해 이해할 수 있다.
    • Promise의 세 가지 상태를 이해할 수 있다.
    • Promise.all의 사용법을 이해할 수 있다.
  • async/await keyword에 대해 이해하고, 작동 원리를 이해할 수 있다.
  • node.js의 fs 모듈의 사용법을 이해한다.

 


비동기 처리란?

 - 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 자바스크립트의 특성을 의미합니다.

출처: 캡틴판교 블로그

 

 

unblocking(비동기 처리)의 예

  • 백그라운드 실행, 로딩 창
  • 인터넷에서의 요청, 큰 용량의 파일 로딩 등의 작업
  • node.js 핵심

 

 

출처: 코드스테이츠

 

 

출처: 코드스테이츠

 

 

클라이언트와 서버의 통신이 비동기적이면 한 처리가 끝날 때까지 기다리지 않고도 여러 가지 일들을 컴퓨터가 동시 다발적으로 처리한다.

유튜브에서 동영상 재생 또한 비동기의 예가 될 수 있다.

만약,

 

(동기적인 예) 동영상이 재생되고 나서 그 외 작업들이 차례대로 되거나 혹은 그 반대 상황을 생각해보자. 사용자들을 꼭 봐야 하는 동영상이 아니라면 이탈률이 클 것이라고 생각할 수 있을 것이다.

 

(비동기적인 예) 그렇다면, 동영상이 버퍼핑(buffering)되는 동안 다른 작업들이 고루 되고, 그다음 버퍼링 후, 동영상이 재생이 된다면 전자(동기적인 예)에 비해서 이탈률도 적을 테고 더 많은 동영상을 볼 가능성(서비스 이용률), 서비스 만족도도 늘 것이라고 추측해볼 수 있을 것 같다.

 

이와 관련되어서 첨언을 한 가지만 하자면, 아무리 비동기로 통신한다고 해도 버퍼링이 어느 정도 발생한다는 이야기를 들었다. 이에 대응하기 위해 "로딩 중"과 같은 문구나 원형 모양으로 돌아가는 이미지(?)를 띄워놓는다고 한다. 

 

 

 


 

 

자, 비동기가 이런 좋은 점이 있다고 대략 알겠는데...

만약, 순서를 제어하고 싶을 경우에는 어떻게 할까?

 

그럴 때을 대비하여 콜백(callback) 함수를 이용할 수 있다.

 

 

#1. 콜백 함수 사용하기 전

const printString = (string) => {
    setTimeout(
      () => {
        console.log(string)
      }, 
      Math.floor(Math.random() * 100) + 1
    )
  }

  const printAll = () => {
    printString("A")
    printString("B")
    printString("C")
  }
  printAll() // what do you expect?

 

 

#1. 콜백 함수 사용하기 전 예시 <결과 보기>

 

 

 

#2. 콜백함수 사용

const printString = (string, callback) => {
    setTimeout(
      () => {
        console.log(string)
        callback()
      }, 
      Math.floor(Math.random() * 100) + 1
    )
  }

  const printAll = () => {
    printString("A", () => {
      printString("B", () => {
        printString("C", () => {})
      })
    })
  }
  printAll() // now what do you expect?

 

 

#2. 콜백함수 사용 예시 <결과 보기>

 

2개의 코드 스니핏들 결과를 보듯이, 

#1의 결과는 A, B, C 가 랜덤 하게 출력되는 것을 볼 수 있고

#2의 결과는 #1와 달리 A, B, C가 순서대로 출력되는 것을 볼 수 있다.

 

 

"이렇게 콜백 함수를 사용하면 특정 로직이 끝났을 때 원하는 동작을 실행시킬 수 있습니다."  - (출처: 캡틴 판교 블로그)
"일반적으로 자바스크립트의 비동기 처리 코드는 아래와 같이 콜백을 사용해야지 코드의 실행 순서를 보장받을 수 있죠."  - (출처: 캡틴 판교 블로그)

 

 

하지만, 비동기 처리를 콜백으로 하다보면 콜백이 콜백을.. 콜백이 콜백을... 이렇게 체인처럼 맞물려서 "콜백 지옥(콜백 헬)"이라는 가독성 떨어지고 유지 보수하기 어려워지는 형태로 탈바꿈되는 경우가 생깁니다.

 

 

출처: 코드스테이츠

 

 

이런 콜백 지옥을 해결하기 위해서는 각 함수를 분리시켜주는 방법과 "Promise"와 "async & await" 키워드들을 이용해서 쉽게 해결할 수 있습니다.

 

 


 

 

Promise란? 

 

Promise란 향후에 언젠가 사용하게 될 값을 생산해내는 객체입니다. 여기서 값은 얻을 수 있거나(resolved), 혹은 값을 얻지 못하는 대신에 그렇게 된 이유를 얻게 됩니다(rejected). 여기서 얻지 못하는 경우는 네트워크 장애 등을 들 수 있겠습니다. Promise는 다음 3가지 상태 중 하나를 가집니다. Fulfilled, Rejected, Pending 중 하나이지요. Promise를 사용할 때에, fulfilled 된 값을 다루는 콜백, 또는 rejected 된 이유를 다루는 콜백을 (인자로) 첨부할 수 있습니다.

출처: cadenzah 블로그

 

 

출처: mdn

 

 

 

프로미스는 콜백과는 다르니게 몇 가지 규칙들이 존재합니다.

  • Callbacks will never be called before the completion of the current run of the JavaScript event loop.
  • Callbacks added with then(), as above, will be called even after the success or failure of the asynchronous operation.
  • Multiple callbacks may be added by calling then() several times. Each callback is executed one after another, in the order in which they were inserted.

출처: mdn (Using Promises)

 

해석해보자면, 

  • 이벤트 루프가 끝나기 전까지 콜백이 호출되지 않습니다.
  • 비동기 처리가 성공이든 실패든, 결과가 나온 후에 then에 추가된 콜백이 호출됩니다.
  • then를 여러 번 사용해서 여러 번 콜백을 추가할 수 있습니다. 각각 콜백은 삽입된 순서대로 차례대로 실행됩니다.  

 

 

Promise Chaning

프로미스 체이닝은. then 키워드를 이용하여 새로운 프로미스(콜백)를 실행시켜서 연속적인 처리가 가능하도록 한다.

여기서 주의해야 할 점이 있는데, 프로미스가 콜백 체인을 해결할 수 있다고 했지만 프로미스 또한 잘못 사용하면 "프로미스 헬"을  만들 수 있으니 주의해야 한다.

 

 

#3. Promise Chaning

function gotoCodestates() {
    return new Promise((resolve, reject) => {
        setTimeout(() => { resolve('1. go to codestates') }, Math.floor(Math.random() * 100) + 1)
    })
}

function sitAndCode() {
    return new Promise((resolve, reject) => {
        setTimeout(() => { resolve('2. sit and code') }, Math.floor(Math.random() * 100) + 1)
    })
}

function eatLunch() {
    return new Promise((resolve, reject) => {
        setTimeout(() => { resolve('3. eat lunch') }, Math.floor(Math.random() * 100) + 1)
    })
}

function goToBed() {
    return new Promise((resolve, reject) => {
        setTimeout(() => { resolve('4. goToBed') }, Math.floor(Math.random() * 100) + 1)
    })
}

const result = async () => {
    const one = await gotoCodestates();
    console.log(one)

    const two = await sitAndCode();
    console.log(two)

    const three = await eatLunch();
    console.log(three)

    const four = await goToBed();
    console.log(four)
}

result();

 

 

#3. Promise Chaning 예시 <결과 보기>

 

프로미스 체인에서 에러 처리는. catch 키워드를 마지막. then 키워드 다음에 삽입하여 에러 핸들링을 한다. 

자세한 내용은 여기여기를 참조할 수 있을 것이다...

 

프라미스와 에러 핸들링

 

ko.javascript.info

 

Promise.reject()에 에러 객체를 넘겨줘야하는 이유

노드에서는 비동기 코드를 작성할 때가 많다. 그래서 콜백헬, 프라미스 키워드가 자주 등장한다. 콜백헬의 대안으로 프라미스를 사용한다는 글도 있지만 꼭 그런것도 아니다. 콜백헬과 프라미

jeonghwan-kim.github.io

 

 


 

 

마지막으로 

async & await이다

 

An async function is a function declared with the async keyword. Async functions are instances of the AsyncFunction constructor, and the await keyword is permitted within them. The async and await keywords enable asynchronous, promise-based behavior to be written in a cleaner style, avoiding the need to explicitly configure promise chains.

출처: mdn(async function)

 

 

해석해보면, async 함수는 async 키워드가 선언된 함수다. 이 함수는 AsyncFunction 생성자의 인스턴스이며, await 키워드는 async 함수 안에서만 가능하다. async, await 키워드는 promise-based behavior와 비동기적이고 깔끔한 스타일로

코드를 짤 수 있다.

 

 

#4. async, await 예시

function gotoCodestates() {
    return new Promise((resolve, reject) => {
        setTimeout(() => { resolve('1. go to codestates') }, Math.floor(Math.random() * 100) + 1)
    })
}

function sitAndCode() {
    return new Promise((resolve, reject) => {
        setTimeout(() => { resolve('2. sit and code') }, Math.floor(Math.random() * 100) + 1)
    })
}

function eatLunch() {
    return new Promise((resolve, reject) => {
        setTimeout(() => { resolve('3. eat lunch') }, Math.floor(Math.random() * 100) + 1)
    })
}

function goToBed() {
    return new Promise((resolve, reject) => {
        setTimeout(() => { resolve('4. goToBed') }, Math.floor(Math.random() * 100) + 1)
    })
}

const result = async () => {
    const one = await gotoCodestates();
    console.log(one)

    const two = await sitAndCode();
    console.log(two)

    const three = await eatLunch();
    console.log(three)

    const four = await goToBed();
    console.log(four)
}

result();

 

 

#4. async, await 예시 <결과 보기>

 

 

 

출처

반응형

'Server > About Server' 카테고리의 다른 글

HTTP vs HTTPS  (0) 2021.05.13
[개념정리]Interaction With Server  (0) 2020.12.17