const runGenerator = (generatorFunction) => {...}
(runGenerator(function* fetchData() {
const url = "https://test.com";
const response = yield fetch(url);
const data = yield response.json();
console.log(data);
})();
// function*은 Generator 함수 키워드이며 하나 이상의 yield 문을 포함한다.
// Generator는 Iterable을 생성하는 함수
async function fetchData() {
const url = "https://test.com";
const response = await fetch(url);
const data = await response.json();
console.log(data);
}
fetchData()
// ES8
async function foo() {
await bar();
}
// ES5 complied
let foo = (() => {
var _ref = _asyncToGenerator(function*() {
yield bar();
});
return function foo() {
return _ref.apply(this, arguments);
};
})();
function _asyncToGenerator(fn) {
return function() {
var gen = fn.apply(this, arguments);
return new Promise(function(resolve, reject) {
function step(key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
return Promise.resolve(value).then(
function(value) {
step("next", value);
},
function(err) {
step("throw", err);
}
);
}
}
return step("next");
});
};
}
async 함수인 foo함수는 즉시실행함수로 바뀌었고, foo함수 안에서 _asyncToGenerator
의 동작이 이루어짐
_asyncToGenerator
는 새로운 Promise
인스턴스를 리턴 ⇒ async 함수는 암묵적으로 내용이 없더라도 promise를 리턴시킴
Promise에서는 fn.apply
를 실행하여 인자로 넘어온 Generator
를 실행하여 iterator
객체를 클로저로 저장해둔다.
나머지는 클로저에 저장한 iterator
를 실행시키고, 반환된 Promise
객체를 재귀함수를 통해 반복실행(next와 else문)한다.
(각각의 await이 각 iterator의 next가 되는 것이다.) 그리고 info.done에 따라 Generator
반복이 끝나면 값을 resolve처리 한다.
정리하자면 async await 패턴은 Generator
와 yield
를 통해 동기적인 모습으로 바꾸어주고, Promise
는 Generator
로 만든 iterator
를 재귀를 통해 반복해서 실행시켜준다.
await
에 사용되는 함수가 항상 Promise
를 반환해야하는 이유await
키워드는 Promise
가 settled
상태(비동기 처리가 수행된 상태)가 될 때까지 대기한 후 resolve
한 처리 결과를 반환하는 표현식이다.
// 순차적인 비동기 통신을 위한 형태
async function bar(n) {
const a = await new Promise(resolve => setTimeout(() => resolve(n), 3000));
// 두 번째 비동기 처리를 위해 첫 번째 비동기 처리 결과가 필요하다.
const b = await new Promise(resolve => setTimeout(() => resolve(a + 1), 3000));
// 세 번째 비동기 처리를 위해 두 번째 비동기 처리 결과가 필요하다.
const c = await new Promise(resolve => setTimeout(() => resolve(b + 1), 3000));
console.log([a, b, c]);
}
bar(1) // 6초 소요
// 순차적인 순서가 필요없을 때의 형태
async function foo() {
const res = await Promise.all([
new Promise(resolve => setTimeout(() => resolve(1), 3000)),
new Promise(resolve => setTimeout(() => resolve(2), 3000)),
new Promise(resolve => setTimeout(() => resolve(3), 3000))
]);
console.log(res); // [1, 2, 3]
}
foo() //3초 소요
Promise
객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.
mdn
Promise
페이지보통 우리가 알고있는
Promise
를 사용하는 이유 중 하나는콜백 지옥
을 해결한다는 것이다. 어떻게 해결한다는 것일까
콜백 지옥
의 문제점은 가독성적인 문제도 있지만, 여기서 짚어볼 문제점은 제어의 역전
이다. 믿음직하지 못한 어떤 비동기 프로그램에게 우리 프로그램의 호출 제어권을 넘겨주어 어떤 일이 발생할 수 있다는 것이다.
제어권이 콜백에게 있는 상태에서 저 함수에서 어떤 일이 잘못된다면..? 어떻게 동작할지, 어떠한 값들이 나올지 모르게 된다.
그래서 이 제어권
을 돌려받기 위해 콜백을 이벤트
방식처럼 사용하는 것이 바로 Promise
이다.
function foo(x) {
// 어떤 일을 실행하고 이벤트를 반환
return listner;
}
var evt = foo(42);
evt.on('completion', bar());
evt.on('failure', barErr());
function foo(x) {
// 어떤 일을 실행하고 프로미스를 반환
return new Promise(function(resolve, reject) {});
}
var p = foo(42);
bar(p);
function bar(fooPromise) {
fooPromise.then(
function() {}, // resolve 시 작업
function() {} // reject 시 작업
);
}
In Promises-land, an important detail is how to know for sure if some value is a genuine Promise or not. Or more directly, is it a value that will behave like a Promise?
- You Don’t Know JS: Async & Performance
하지만 그런 신뢰도 때문에 Promise
를 만들었지만, 그 Promise
가 진짜 Promise
인줄 어떻게 알까요..?
만약에 Promise
가 아닌 then을 품고 그 안에 fulfilled
와 rejected
를 담고있는 객체 라면?
var p = {
then: function(foo, err) {
foo(42);
err('하이');
}
};
p.then(
function fulfilled(val) {
console.log(val);
},
function rejected(err) {
console.log(err);
}
);
자바스크립트는 이를 덕 타이핑
이라는 특징으로 이 신뢰를 해결합니다.
덕 타이핑 : 오리처럼 생긴 동물이 오리처럼 운다면, 그것은 오리로 생각하겠다는 의미입니다. 코드적으로 풀어보면 instanceof 같은 키워드로 관계를 조사하는 대신에, 객체가 갖추고 있어야 할 일정한 조건을 만족하면 그것을 대상 객체처럼 사용하겠다는 뜻입니다.
이 특성은 Promise
에서는 then()
메소드를 갖고있는 Thenable
한 객체라면 모두 Promise
로 간주하고 동작한다는 의미로 쓰입니다.
안전하게 Promise
를 사용하는 방법 ⇒ Promise.resolve
를 사용해 감싸자
// foo가 실제 Promise인지, 아니면 then을 갖춘 가짜 Promise 객체인지 알 수 없다.
foo( 42 )
.then( function(v){
console.log( v );
} );
// Promise.resolve는 foo가 Thenable한 객체라면 Promise로 간주하고 사용함으로써
// 이것이 Promise라는 신뢰를 얻을 수 있다.
Promise.resolve( foo( 42 ) )
.then( function(v){
console.log( v );
} );
foo라는 객체가 진짜 Promise
인지 가짜 Promise
인지는 모르지만 최소한 Thenable
한 것은 알고있으니 이런식으로 사용해야 Promise
의 의미인 신뢰를 살려 사용할 수 있다.