Dali
4.5
function *foo() {
console.log( "`*foo()` starting" );
yield 3;
yield 4;
console.log( "`*foo()` finished" );
}
function *bar() {
yield 1;
yield 2;
yield *foo(); // `yield`-delegation!
yield 5;
}
var it = bar();
it.next().value; // 1
it.next().value; // 2
it.next().value; // `*foo()` starting
// 3
it.next().value; // 4
it.next().value; // `*foo()` finished
// 5
안에서 제어권 * generator를 넘기면 foo를 완료 한 후에
다시 bar로 넘어 온다.
4.5
run (foo) 같은 처리 보다
* foo가 가독성, 유지보수 여러 측면에서 더 유리
Why?
4.5.2 메시지 위임
function *foo() {
console.log( "inside `*foo()`:", yield "B" );
console.log( "inside `*foo()`:", yield "C" );
return "D";
}
function *bar() {
console.log( "inside `*bar()`:", yield "A" );
// `yield`-delegation!
console.log( "inside `*bar()`:", yield *foo() );
console.log( "inside `*bar()`:", yield "E" );
return "F";
}
var it = bar();
console.log( "outside:", it.next().value );
// outside: A
console.log( "outside:", it.next( 1 ).value );
// inside `*bar()`: 1
// outside: B
console.log( "outside:", it.next( 2 ).value );
// inside `*foo()`: 2
// outside: C
console.log( "outside:", it.next( 3 ).value );
// inside `*foo()`: 3
// inside `*bar()`: D
// outside: E
console.log( "outside:", it.next( 4 ).value );
// inside `*bar()`: 4
// outside: F
4.5
next(3), *[1,2,3]
function *foo() {
console.log( "inside `*foo()`:", yield "B" );
console.log( "inside `*foo()`:", yield "C" );
return "D";
}
function *bar() {
console.log( "inside `*bar()`:", yield "A" );
// `yield`-delegation!
console.log( "inside `*bar()`:", yield *foo() );
console.log( "inside `*bar()`:", yield "E" );
return "F";
}
var it = bar();
console.log( "outside:", it.next().value );
// outside: A
console.log( "outside:", it.next( 1 ).value );
// inside `*bar()`: 1
// outside: B
console.log( "outside:", it.next( 2 ).value );
// inside `*foo()`: 2
// outside: C
console.log( "outside:", it.next( 3 ).value );
// inside `*foo()`: 3
// inside `*bar()`: D
// outside: E
console.log( "outside:", it.next( 4 ).value );
// inside `*bar()`: 4
// outside: F
4.5
Error처리
function *foo() {
try {
yield "B";
}
catch (err) {
console.log( "error caught inside `*foo()`:", err );
}
yield "C";
throw "D";
}
function *bar() {
yield "A";
try {
yield *foo();
}
catch (err) {
console.log( "error caught inside `*bar()`:", err );
}
yield "E";
yield *baz();
// note: can't get here!
yield "G";
}
function *baz() {
throw "F";
}
var it = bar();
console.log( "outside:", it.next().value );
// outside: A
console.log( "outside:", it.next( 1 ).value );
// outside: B
console.log( "outside:", it.throw( 2 ).value );
// error caught inside `*foo()`: 2
// outside: C
console.log( "outside:", it.next( 3 ).value );
// error caught inside `*bar()`: D
// outside: E
try {
console.log( "outside:", it.next( 4 ).value );
}
catch (err) {
console.log( "error caught outside:", err );
}
// error caught outside: F
4.5
재귀
function *foo(val) {
if (val > 1) {
// generator recursion
val = yield *foo( val - 1 );
}
return yield request( "http://some.url/?v=" + val );
}
function *bar() {
var r1 = yield *foo( 3 );
console.log( r1 );
}
run( bar )
4.6
제너레이터 동시성
function request(url){
return fetch(url).then(res=>res.json())
}
var res = [];
function *reqData(url) {
debugger;
res.push(
yield request( url )
);
}
var it1 = reqData(
"https://api.github.com/search/repositories?q=language:javascript&sort=stars&per_page=1`"
);
var it2 = reqData(
"https://api.github.com/search/repositories?q=language:javascript&sort=stars&per_page=2`"
);
var p1 = it1.next().value;
var p2 = it2.next().value;
p1
.then( function(data){
it1.next( data );
return p2;
} )
.then( function(data){
it2.next( data );
} );
4.6
제너레이터 동시성
function request(url){
return fetch(url).then(res=>res.json())
}
var res = [];
function *reqData(url) {
debugger;
var data = yield request( url )
yield;
res.push(data);
}
var it1 = reqData( "https://api.github.com/search/repositories?q=language:javascript&sort=stars&per_page=1`" );
var it2 = reqData( "https://api.github.com/search/repositories?q=language:javascript&sort=stars&per_page=2`" );
var p1 = it1.next().value;
var p2 = it2.next().value;
p1
.then( function(data){
it1.next( data );
} )
p2.then( function(data){
it2.next( data );
} );
Promise.all([p1,p2]).then(()=>{
it1.next()
it2.next()
})
4.6
제너레이터 동시성
// `request(..)` is a Promise-aware Ajax utility
var res = [];
function *reqData(url) {
var data = yield request( url );
// transfer control
yield;
res.push( data );
}
var it1 = reqData( "http://some.url.1" );
var it2 = reqData( "http://some.url.2" );
var p1 = it1.next().value;
var p2 = it2.next().value;
p1.then( function(data){
it1.next( data );
} );
p2.then( function(data){
it2.next( data );
} );
Promise.all( [p1,p2] )
.then( function(){
it1.next();
it2.next();
} );
4.6
제너레이터 동시성
var res = [];
runAll(
function*(){
var p1 = request( "http://some.url.1" );
// transfer control
yield;
res.push( yield p1 );
},
function*(){
var p2 = request( "http://some.url.2" );
// transfer control
yield;
res.push( yield p2 );
}
);
4.6
제너레이터 동시성
runAll(
function*(data){
data.res = [];
// transfer control (and message pass)
var url1 = yield "http://some.url.2";
var p1 = request( url1 ); // "http://some.url.1"
// transfer control
yield;
data.res.push( yield p1 );
},
function*(data){
// transfer control (and message pass)
var url2 = yield "http://some.url.1";
var p2 = request( url2 ); // "http://some.url.2"
// transfer control
yield;
data.res.push( yield p2 );
}
);
4.7
썽크
썽크: 다른 함수를 호출할 운명을 가진 인자 없는 함수
function foo(x,y) {
return x + y;
}
function fooThunk() {
return foo( 3, 4 );
}
// later
console.log( fooThunk() ); // 7
참고 프로미스와 같이 이런 패턴도 있다.
4.7
썽크
썽크: 비동기
function foo(x,y,cb) {
setTimeout( function(){
cb( x + y );
}, 1000 );
}
function fooThunk(cb) {
foo( 3, 4, cb );
}
// later
fooThunk( function(sum){
console.log( sum ); // 7
} );
4.7
썽크
function thunkify(fn) {
var args = [].slice.call( arguments, 1 );
return function(cb) {
args.push( cb );
return fn.apply( null, args );
};
}
var fooThunk = thunkify( foo, 3, 4 );
// later
fooThunk( function(sum) {
console.log( sum ); // 7
} );
function foo(x,y,cb) {
setTimeout( function(){
cb( x + y );
}, 1000 );
}
4.7
썽커리?
function thunkify(fn) {
return function() {
var args = [].slice.call( arguments );
return function(cb) {
args.push( cb );
return fn.apply( null, args );
};
};
}
var whatIsThis = thunkify( foo );
var fooThunk = whatIsThis( 3, 4 );
// later
fooThunk( function(sum) {
console.log( sum ); // 7
} );
4.7
썽크
var fooThunkory = thunkify( foo );
var fooThunk1 = fooThunkory( 3, 4 );
var fooThunk2 = fooThunkory( 5, 6 );
// later
fooThunk1( function(sum) {
console.log( sum ); // 7
} );
fooThunk2( function(sum) {
console.log( sum ); // 11
} );
// cleaner:
var fooThunkory = thunkify( foo );
var fooThunk1 = fooThunkory( 3, 4 );
var fooThunk2 = fooThunkory( 5, 6 );
// instead of:
var fooThunk1 = thunkify( foo, 3, 4 );
var fooThunk2 = thunkify( foo, 5, 6 );
4.7
Promise/Thunk
Generator+ 비동기 thunk / PromiseWrap
// symmetrical: constructing the question asker
var fooThunkory = thunkify( foo );
var fooPromisory = promisify( foo );
// symmetrical: asking the question
var fooThunk = fooThunkory( 3, 4 );
var fooPromise = fooPromisory( 3, 4 );
// get the thunk answer
fooThunk( function(err,sum){
if (err) {
console.error( err );
}
else {
console.log( sum ); // 7
}
} );
// get the promise answer
fooPromise
.then(
function(sum){
console.log( sum ); // 7
},
function(err){
console.error( err );
}
);
4.7
Promise/Thunk
Generator+ 비동기 thunk / PromiseWrap
대칭적으로 괜찮아보이지만
전박적으로 프로미스가 제공하는 혜택을 생각하면 Promise 위주로 사용
// promisory `request(..)` (see Chapter 3)
var request = Promise.wrap( ajax );
// vs.
// thunkory `request(..)`
var request = thunkify( ajax );
4.8
ES6 이전 제너레이터
function foo(url) {
// ..
// make and return an iterator
return {
next: function(v) {
// ..
},
throw: function(e) {
// ..
}
};
}
var it = foo( "http://some.url.1" );
이터레이터를 만들어 반환 상태는 Closure를 통해서 조작
4.8
ES6 이전 제너레이터
이터레이터를 만들어 반환 상태는 Closure를 통해서 조작
class DoubleMaker {
constructor(data) {
this.data = data;
}
[Symbol.iterator]() {
let idx = 0;
const data = this.data;
const dataLength = this.data.length;
return {
next() {
if( idx >= dataLength) return {value : undefined, done:true};
else return {value : Math.pow(data[idx++],2), done:false};
}
}
}
}
//하나씩 컨트롤
const dbl = new DoubleMaker([1,2,3,4,5]);
const dblIterator = dbl[Symbol.iterator]();
dblIterator.next().value; //1
dblIterator.next().value; //4
dblIterator.next().value; //9
//전체 출력하려면,
const dbl = new DoubleMaker([1,2,3,4,5]);
for(v of dbl) {
console.log(v);
}
4.8
자동변환
제너레이터 비동기 흐름을 동기적으로 가져가고
제어권를 조작할 수 있어서 매력적이다 !!
var _marked =
/*#__PURE__*/
regeneratorRuntime.mark(range);
function range(max, step) {
var count, i;
return regeneratorRuntime.wrap(function range$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
count = 0;
step = step || 1;
i = 0;
case 3:
if (!(i < max)) {
_context.next = 10;
break;
}
count++;
_context.next = 7;
return i;
case 7:
i += step;
_context.next = 3;
break;
case 10:
return _context.abrupt("return", count);
case 11:
case "end":
return _context.stop();
}
}
}, _marked, this);
}
var gen = range(20, 3),
info;
while (!(info = gen.next()).done) {
console.log(info.value);
}
console.log("steps taken: " + info.value);