Generator :D .5~.9

Dali

Gen Delegation

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);

Generator :D .5~.9

By chany

Generator :D .5~.9

  • 356