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