비동기 프로그래밍

Jongmoon Yoon
9 min readNov 15, 2015

--

  • 싱글스레드
    - 한번에 하나의 스크립트만 실행 가능
    - Queue 로 관리
    → 결국 오래걸리는 작업은 비동기로 처리해야 함.
  • ES5 까지 비동기 처리를 위한 2가지 패턴
    - callback
    - event handler

callback 이나 event 방식 결국 동일한거 아닌가?

Event 방식

  • 이슈
    1. 모든 비동기 처리 블록에 대한 개별적인 Exception 처리 필요
    2. Nested function call 에 의해 코드읽기가 어려움
    3. 다른 파트에서 비동기 동작에 대한 상태관리를 하기 위해서 별도 변수를 관리해야 함 → 상태 관리 어려움

Callback 방식

성공 실패시 전달된 success 혹은 error callback 으로 결과를 알려줌.

  • 이슈
    - Event 방식과 동일

두 가지 방식(Event, Callback) 모두 중첩된 Ajax 혹은 다른 비동기 작업이 추가 되는 경우 복잡도가 증가

Callback Hell (출처: http://www.slideshare.net/wookieb/callbacks-promises-generators-asynchronous-javascript)

Promise

  • 위 이슈를 해결하고자 ES6 에 도입
    1. 개별적인 Exception 처리 불편
    2. Nested Function Call 에 의한 코드 읽기 어려움
    3. 비동기 동작에 대한 상태관리
  • 이미 다른 언어 (Java — Future, C# 5, C++ 11, Swift, Scala …)에서 도입, Since 1976

Promises 생성자

비동기 처리를 수행할 내용이 있는 callback(executor) 를 파라미터로 받고 executor 는 resolve 와 reject 를 인자로 받는다.

  • resolve : 비동기 작업이 성공적으로 수행시 호출되는 Callback 함수
  • reject: 비동기 작업이 실패한 경우 호출되는 Callback 함수

Promise 의 상태값
위 resolve/reject 호출에 따라 다음과 같은 4개의 상태값으로 구분할 수 있다.

  1. Fulfilled: resolve(non-Promise object or no arguments) 로 호출된 경우
  2. Rejected: reject 가 호출되거나 executor 안에서 예외가 발생한 경우
  3. Pending: resolve 혹은 reject 가 아직 호출되지 않은 상태
  4. Settled: fulfilled 되거나 rejected 된 경우(pending 되지 않은 상태)

한번 fulfilled 되거나 rejected 되면 결과를 되돌릴 수 없다.

Promise 객체를 인자로 resolve 가 호출된 경우 Promise Object 는 전달된 Promise 의 상태를 따른다.

fulfillment value

즉 resolve 의 파라미터

  • 해당 값이 Promise 가 아닌 경우 그 값 자체가 Promise 객체의 fulfillment value 로 처리
  • 아무것도 전달되지 않은 경우 value 는 undefined,지만 Promise 는 fulfilled 되었다고 간주
  • Promise 객체가 전달된 경우 전달된 Promise 객체가 fulfilled 된 경우에 fulfilled 된 값이 fulfillment value 가 된다.
    마찬가지로 전달된 Promise 객체가 reject 된 경우 Reason 은 전달된 Promise 객체의 reject Reason 과 동일하다.

then(onFulfilled, onRejected)

Promise 객체의 then 메소드

Promise 가 fulfilled 혹은 reject 된 후 후처리를 할 수 있게 해준다.

2개의 파라미터

  • onFulfilled
    - Promise 객체가 fulfilled 되었을 때 호출되는 callback
    - 인자는 Promise 객체의 fulfillment value
  • onRejected
    - Promise 객체가 rejected 되었을때 호출되는 callback
    - executor 범위(scope) 안에서 exception 이 발생한 경우에도 호출됨

then()메소드에 전달되는 callback 은 비동기적으로 처리된다.

아래와 같은 promise 객체를 생략한 형태로 많이 쓴다. 이런 스타일을 통해 가독성을 높인다.

then() 은 항상 새롭게 생성된 promise 객체를 반환한다. 이 객체는 callback 의 반환 값을 resolve 한다.

다음은 then() 에서 새롭게 생성된 promise 객체가 어떻게 반환되는지를 설명한다.

onFulfilled Callback 의 종류에 따른 처리 방식

  • callback = onFulilled (no return)
    → 내부적으로 새로운 fulfilled Promise가 생성/반환
    → 인자 없이 resolve
  • callback =onFulfilled (return ‘custom Promise’)
    → 내부적으로 새로운 Promise객체 생성/반환
    → (이 객체는) ‘custom Promise’를 resolve

위에서 Promise 객체를 resolve 하면 어떻게 된다고 했죠?

  • callback = onFulfilled (return !‘custom Promise’)
    → 내부적으로 새로운 Promise 객체 생성/반환
    → 위의 반환값을 resolve
  • callback = null
    → onFulfilled 이 내부적으로 생성되어 null 을 대체
    → 내부적으로 새로운 Promise 객체 생성/반환
    → 부모 Promise 의 fulillment value 와 동일하다.

onRejected Callback 의 종류에 따른 처리 방식

  • callback = onRejected (no return)
    → 내부적으로 새로운 Promise 객체 생성/반환
    → 인자 없이 resolve
  • callback = onRejected (return ‘custom Promise’)
    → 내부적으로 새로운 Promise 객체 생성/반환
    → ‘custom Promise’를 resolve
  • callback = onRejected (return !‘custom Promise’)
    → 내부적으로 새로운 Promise 객체가 생성/반환
    → (이 객체가) 위 반환값을 resolve
  • callback = null or 생략
    → onRejected Callback 이 자동 생성
    → (이 callback은) 새로운 rejected Promise 객체를 반환
    → rejection 의 reason 은 부모 Promise 객체의 reason 과 동일
에러 처리를 마지막에 한번만 해주어도 됨

다음은 다중 체이닝 실행이 어떻게 되는지 보여주는 그림이다.

https://mdn.mozillademos.org/files/8633/promises.png

catch(onRejected)

에러만을 다룰 때 then() 대신 사용되는 함수 (단지 그것뿐. 가독성 향상)

항상 새로운 promise 객체가 생성되며 다음은 그 생성 방식이다.

onRejected 의 반환값에 따라

  • no return → 새로운 fulfilled Promise 를 내부적으로 생성/반환
  • return custom Promise → 새로운 Promise 객체가 내부적으로 생성/반환 → (이 객체는) custom Promise 객체를 resolve
  • return !custom Promise → 새로운 Promise 객체가 내부적으로 생성/반환 → (이 객체는) return value 를 resolve
  • null 혹은 생략 → onRejected 가 내부적으로 생성되어 null 을 대체 → (이 콜백은) rejected Proimse 객체를 반환, reason 은 부모를 따른다.
  • catch 가 수행될 promise 객체가 fulfilled 되면, catch()는 그냥 새로운 fulfilled promise 객체를 반환하고, onRejected 는 무시한다. fulfillment value 는 부모를 따른다.

Promise.resolve(value)

value 를 resolve 하는 Promise 객체를 반환한다.

  • value 를 Promise 객체로 변환하기 위해 사용
  • value 가 Promise 객체인지 불확실 할때 사용하면 유용
    - eg. ES6 의 Promise 와 다른 인터페이스를 갖는 jQuery Promise 를 ES6 로 변환

Promise.reject(value)

value 를 reason 으로 한 rejected Promise 를 반환한다.

디버깅용으로 사용. (resolve 와 목적이 다름)

Promise.all(iterable)

Promise 객체 리스트(iterable)이 fulfilled 되었을때 fulfilled 되는 Promise 객체를 반환한다.

만약 iterable 리스트에 Promise 객체가 아닌 값이 있는 경우 Promise.resolve() 에 의해 Promise 로 변환된다.

여러 개의 비동기 작업들이 완료된 후 해야할 작업이 있을 때 유용

Rejected 되는 경우, 실패 즉시 rejected Promise 객체를 반환하고 실패한 Promise 의 reason 을 reason 으로 사용한다.

Promise.race(iterable)

Promise 객체 리스트(iterable) 중 가장 먼저 fulfilled 혹은 rejected 되는 Promise 객체를 반환한다.

--

--