TypeScript Deep Dive
  • README
  • 시작하기
    • 왜 타입스크립트인가
  • 자바스크립트
    • 비교 연산자
    • 참조 연산자
    • Null vs. Undefined
    • this
    • 클로저
    • Number
    • Truthy
  • 미래의 자바스크립트
    • 클래스
      • 즉시실행함수
    • 화살표 함수
    • 나머지 연산자
    • let
    • const
    • 비구조화 할당
    • 전개 연산자
    • for...of
    • 이터레이터
    • 템플릿 리터럴
    • 프로미스
    • 제네레이터
    • Async Await
  • 프로젝트
    • 컴파일러 제어
      • tsconfig.json
      • 파일 경로 지정
    • 선언
    • 모듈화
      • 파일을 이용한 모듈화
      • globals.d.ts
    • 네임스페이스
    • 동적 표현식 가져오기
  • Node.js 시작하기
  • Browser 시작하기
  • 타입스크립트 타입 시스템
    • 자바스크립트 마이그레이션 가이드
    • @types
    • 주변 선언
      • 파일 선언
      • 변수
    • 인터페이스
    • 열거형(Enums)
    • lib.d.ts
    • 함수
    • 콜러블(Callable)
    • 타입 표명(Type Assertion)
    • 신선도(Freshness)
    • 타입 가드
    • 리터럴(Literal)
    • 읽기 전용(readonly)
    • 제네릭
    • 타입 인터페이스
    • 타입 호환성
    • Never 타입
    • 구별된 유니온
    • 인덱스 서명(Index Signature)
    • 타입 이동하기
    • 예외 처리
    • 믹스인(Mixin)
  • JSX
    • React
    • Non React JSX
  • Options
    • noImplicitAny
    • strictNullChecks
  • 타입스크립트 에러
    • 에러 메세지
    • 공통 에러
  • NPM
  • 테스트
    • Jest
    • Cypress
  • Tools
    • Prettier
    • Husky
    • ESLint
    • Changelog
  • 팁
    • 문자열 Enums
    • 타입 단언
    • 상태 저장 함수
    • 커링
    • 제네릭 타입 예시
    • 객체 타입 설정
    • 유용한 클래스
    • Import / Export
    • 속성 Setters
    • outFile 주의사항
    • 제이쿼리 팁
    • 정적 생성자
    • 싱글톤 패턴
    • 함수 파라미터
    • 토글 생성
    • Import 여러개 하기
    • 배열 생성
    • 생성자에서 타입정의
  • 스타일 가이드
  • 타입스크립트 컴파일러 구조
    • Program
    • AST
      • TIP: Visit Children
      • TIP: SyntaxKind enum
      • Trivia
    • Scanner
    • Parser
      • Parser Functions
    • Binder
      • Binder Functions
      • Binder Declarations
      • Binder Container
      • Binder SymbolTable
      • Binder Error Reporting
    • Checker
      • Checker Diagnostics
      • Checker Error Reporting
    • Emitter
      • Emitter Functions
      • Emitter SourceMaps
    • Contributing
Powered by GitBook
On this page
  • Generated JavaScript
  • Async Await Support in TypeScript

Was this helpful?

  1. 미래의 자바스크립트

Async Await

동일한 자료를 다루는 프로 에그비디오 코스

Promise가 사용될때 await 키워드에서 코드 실행을 일시적으로 중지하고, 함수에서 반환된 Promise가 가능한 한번만 실행되도록 Javascript 런타임에 명령하는 방법을 실험으로 가정해봅니다:

// Not actual code. A thought experiment
async function foo() {
    try {
        var val = await getMeAPromise()
        console.log(val)
    } catch (err) {
        console.log('Error: ', err.message)
    }
}

Promise가 수행을 계속한다면,

  • 만약 await가 실행된다면, 값을 반환하며,

  • 에러로 인하여 reject 된다면 catch에서 잡을 수 있도록 에러를 반환합니다.

이러한 수행은 갑자기 (신기할정도로) 비동기 프로그램을 동기프로그래밍처럼 쉽게 만들고 있습니다. 이러한 실험에는 세가지 사항이 필요합니다.

  • 함수를 일시 중지하는 기능

  • 함수안의 값을 출력하는 기능

  • 함수내의 예외를 던질 수 있는 기능

이것은 꼭 제네레이터가 실행되는 원리 같습니다! 이러한 실험은 실제 발생할 수 있으며, Typescript / Javascript의 async/await에 대한 구현입니다. 실제로 async/await는 제네레이터를 사용하여 구현됩니다.

Generated JavaScript

이 원리를 꼭 이해할 필요는 없지만, 제네레이터를 읽었다면, 간단히 이해할 수 있습니다. foo 함수는 다음과 같이 간단히 정리할 수 있습니다.

const foo = wrapToReturnPromise(function*() {
    try {
        var val = yield getMeAPromise()
        console.log(val)
    } catch (err) {
        console.log('Error: ', err.message)
    }
})

wrapToReturnPromise는 제네레이터 함수를 사용하여 generator 객체를 반환받은 다음, generator.next()를 사용합니다. 만약 값이 promise 라면 then+catch하고 결과값을 generator.next(result)또는 generator.thorw(error) 로 호출해야 합니다. 그게 전부입니다.

Async Await Support in TypeScript

Async - Await 다음을 지원합니다. TypeScript since version 1.7. 비동기는 함수앞에는 async 키워드가 접두어로 붙습니다. await는 비동기 함수가 프로미스를 반환할때까지 실행을 일시 중단하고 반환된 프로미스 내부로부터 값을 반환받을 수 있습니다.

ES6 generators는 트랜스파일링을 통해 오직 es6를 지원합니다.

TypeScript 2.1 added the capability to ES3 and ES5 run-times은 ES3 및 ES5 런타임에 제네레이터 기능을 추가했습니다. 즉, 사용중인 환경에 관계없이 자유롭게 활용이 가능합니다. Typescript2.1에서 async/await를 사용할 수 잇으며, Promise에 대한 polyfill을 전역으로 추가하여 다양한 브라우저에 대응할 수 있습니다.

예제를 보고 타입스크립트에서 async /await 표기법이 어떻게 작동하는지 알아봅니다.

function delay(milliseconds: number, count: number): Promise<number> {
    return new Promise<number>(resolve => {
        setTimeout(() => {
            resolve(count)
        }, milliseconds)
    })
}

// async function always returns a Promise
async function dramaticWelcome(): Promise<void> {
    console.log('Hello')

    for (let i = 0; i < 5; i++) {
        // await is converting Promise<number> into number
        const count: number = await delay(500, i)
        console.log(count)
    }

    console.log('World!')
}

dramaticWelcome()

ES6로 트랜스파일링하기 (--target es6)

var __awaiter =
    (this && this.__awaiter) ||
    function(thisArg, _arguments, P, generator) {
        return new (P || (P = Promise))(function(resolve, reject) {
            function fulfilled(value) {
                try {
                    step(generator.next(value))
                } catch (e) {
                    reject(e)
                }
            }
            function rejected(value) {
                try {
                    step(generator['throw'](value))
                } catch (e) {
                    reject(e)
                }
            }
            function step(result) {
                result.done
                    ? resolve(result.value)
                    : new P(function(resolve) {
                          resolve(result.value)
                      }).then(fulfilled, rejected)
            }
            step(
                (generator = generator.apply(thisArg, _arguments || [])).next()
            )
        })
    }
function delay(milliseconds, count) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(count)
        }, milliseconds)
    })
}
// async function always returns a Promise
function dramaticWelcome() {
    return __awaiter(this, void 0, void 0, function*() {
        console.log('Hello')
        for (let i = 0; i < 5; i++) {
            // await is converting Promise<number> into number
            const count = yield delay(500, i)
            console.log(count)
        }
        console.log('World!')
    })
}
dramaticWelcome()

전체 예제는 여기에서 확인하실 수 있습니다.

ES5로 트랜스파일링 하기 (--target es5)

var __awaiter =
    (this && this.__awaiter) ||
    function(thisArg, _arguments, P, generator) {
        return new (P || (P = Promise))(function(resolve, reject) {
            function fulfilled(value) {
                try {
                    step(generator.next(value))
                } catch (e) {
                    reject(e)
                }
            }
            function rejected(value) {
                try {
                    step(generator['throw'](value))
                } catch (e) {
                    reject(e)
                }
            }
            function step(result) {
                result.done
                    ? resolve(result.value)
                    : new P(function(resolve) {
                          resolve(result.value)
                      }).then(fulfilled, rejected)
            }
            step(
                (generator = generator.apply(thisArg, _arguments || [])).next()
            )
        })
    }
var __generator =
    (this && this.__generator) ||
    function(thisArg, body) {
        var _ = {
                label: 0,
                sent: function() {
                    if (t[0] & 1) throw t[1]
                    return t[1]
                },
                trys: [],
                ops: []
            },
            f,
            y,
            t,
            g
        return (
            (g = { next: verb(0), throw: verb(1), return: verb(2) }),
            typeof Symbol === 'function' &&
                (g[Symbol.iterator] = function() {
                    return this
                }),
            g
        )
        function verb(n) {
            return function(v) {
                return step([n, v])
            }
        }
        function step(op) {
            if (f) throw new TypeError('Generator is already executing.')
            while (_)
                try {
                    if (
                        ((f = 1),
                        y &&
                            (t =
                                y[
                                    op[0] & 2
                                        ? 'return'
                                        : op[0]
                                        ? 'throw'
                                        : 'next'
                                ]) &&
                            !(t = t.call(y, op[1])).done)
                    )
                        return t
                    if (((y = 0), t)) op = [0, t.value]
                    switch (op[0]) {
                        case 0:
                        case 1:
                            t = op
                            break
                        case 4:
                            _.label++
                            return { value: op[1], done: false }
                        case 5:
                            _.label++
                            y = op[1]
                            op = [0]
                            continue
                        case 7:
                            op = _.ops.pop()
                            _.trys.pop()
                            continue
                        default:
                            if (
                                !((t = _.trys),
                                (t = t.length > 0 && t[t.length - 1])) &&
                                (op[0] === 6 || op[0] === 2)
                            ) {
                                _ = 0
                                continue
                            }
                            if (
                                op[0] === 3 &&
                                (!t || (op[1] > t[0] && op[1] < t[3]))
                            ) {
                                _.label = op[1]
                                break
                            }
                            if (op[0] === 6 && _.label < t[1]) {
                                _.label = t[1]
                                t = op
                                break
                            }
                            if (t && _.label < t[2]) {
                                _.label = t[2]
                                _.ops.push(op)
                                break
                            }
                            if (t[2]) _.ops.pop()
                            _.trys.pop()
                            continue
                    }
                    op = body.call(thisArg, _)
                } catch (e) {
                    op = [6, e]
                    y = 0
                } finally {
                    f = t = 0
                }
            if (op[0] & 5) throw op[1]
            return { value: op[0] ? op[1] : void 0, done: true }
        }
    }
function delay(milliseconds, count) {
    return new Promise(function(resolve) {
        setTimeout(function() {
            resolve(count)
        }, milliseconds)
    })
}
// async function always returns a Promise
function dramaticWelcome() {
    return __awaiter(this, void 0, void 0, function() {
        var i, count
        return __generator(this, function(_a) {
            switch (_a.label) {
                case 0:
                    console.log('Hello')
                    i = 0
                    _a.label = 1
                case 1:
                    if (!(i < 5)) return [3 /*break*/, 4]
                    return [4 /*yield*/, delay(500, i)]
                case 2:
                    count = _a.sent()
                    console.log(count)
                    _a.label = 3
                case 3:
                    i++
                    return [3 /*break*/, 1]
                case 4:
                    console.log('World!')
                    return [2 /*return*/]
            }
        })
    })
}
dramaticWelcome()

전체 예제는 여기에서 확인하실 수 있습니다.

참고: 두가지(ES6, ES5) 시나리오 모두에 대해서 런타임에 ECMAScript가 호환되는 Promise를 전역에 사용할 수 있는지 확인해야 합니다. 이 경우 Promise용 polyfill을 설정해야 할 수도 있습니다. 또한, tsc내 lib 플래그를 "dom", "es2015" 또는 "dom", "es2015.promise", "es5"와 같은 형식으로 설정하여 Typescript에 Promise가 포함되어 있음을 확인해야 합니다. 우리는 브라우저가 native 혹은 polyfill을 이용하여 프로미스를 지원하는지 볼수 있습니다. 여기를 클릭해주세요

Previous제네레이터Next프로젝트

Last updated 5 years ago

Was this helpful?