Closed sailei1 closed 5 years ago
生成器 生成器就是一类特殊的函数,可以一次或多次启动和停止,并不一定非要完成。
function *foo(x) { var y = x * (yield); return y; } var it = foo( 6 ); // 启动foo(..) it.next(); var res = it.next( 7 ); res.value; // 42
首先,传入 6 作为参数 x。然后调用 it.next(),这会启动 foo(..)。 在foo(..)内部,开始执行语句var y = x ..,但随后就遇到了一个yield表达式。它 就会在这一点上暂停 foo(..)(在赋值语句中间!),并在本质上要求调用代码为 yield 表达式提供一个结果值。接下来,调用it.next( 7 ),这一句把值7传回作为被暂停的 yield 表达式的结果。 所以,这时赋值语句实际上就是var y = 6 7。现在,return y返回值42作为调用 it.next( 7 ) 的结果。 注意,这里有一点非常重要。
yield 会导致生成器在执行过程中发送出一个值,这有点类似于中间的 return。
第一个 next(..) 总是启动一个生成器,并运行到第一个 yield 处。不过,是第二个 next(..) 调用完成第一个被暂停的 yield 表达式,第三个 next(..) 调用完成第二个 yield, 以此类推。
yield 并不会阻塞整个程序,它只是暂停或阻塞了生成器本身的代码。
迭代器 迭代器是一个定 义良好的接口,用于从一个生产者一步步得到一系列值。
// for..of循环需要 [Symbol.iterator]: function(){ return this; },
try catch 如果在生成器内有 try..finally 语句,它将总是运行,即使生成器已经外部结束。
function *something() { try { var nextVal; while (true) { if (nextVal === undefined) { nextVal = 1; }else { nextVal = (3 * nextVal) + 6; } yield nextVal; } }finally { console.log( "cleaning up!" ); } } var it = something(); for (var v of it) { console.log( v ); if (v > 500) { console.log(it.return( "Hello World" ).value); // break; break也会触发 } } // 1 9 33 105 321 969 // cleaning up! // Hello World // for..of 循环内的 break 会触发 finally 语句。但是,也可以在外部通过return(..) 手工终止生成器的迭代器实例
获得 Promise 和生成器最大效用的最自然的方法就是 yield 出来一个 Promise,然后通过这个 Promise 来控制生成器的迭代器。
function foo(x,y) { return request( "http://some.url.1/?x=" + x + "&y=" + y //返回一个promise ); } function *main() { try { var text = yield foo( 11, 31 ); console.log( text ); } catch (err) { console.error( err ); } } var it = main(); var p = it.next().value; // 等待promisep决议 p.then( function(text){ it.next( text ); }, function(err){ it.throw( err ); } );
async 与 await async 函数。最后,我们不再 yield 出 Promise,而是用 await 等待它决议 如果你 await 了一个 Promise,async 函数就会自动获知要做什么,它会暂停这个函数(就 像生成器一样),直到 Promise 决议。
Promise 并发
function run(gen) { var args = [].slice.call( arguments, 1), it; // 在当前上下文中初始化生成器 it = gen.apply( this, args ); // 返回一个promise用于生成器完成 return Promise.resolve() .then( function handleNext(value){ // 对下一个yield出的值运行 var next = it.next( value ); return (function handleResult(next){ // 生成器运行完毕了吗? if (next.done) { return next.value; } // 否则继续运行 else { return Promise.resolve( next.value ) .then( // 成功就恢复异步循环,把决议的值发回生成器 handleNext, // 如果value是被拒绝的 promise, // 就把错误传回生成器进行出错处理 function handleErr(err) { return Promise.resolve( it.throw( err )).then( handleResult ); } ); } })(next); }); } function *foo() { // 让两个请求"并行" var p1 = request( "http://some.url.1" ); var p2 = request( "http://some.url.2" ); // 等待两个promise都决议 var r1 = yield p1; var r2 = yield p2; var r3 = yield request( "http://some.url.3/?v=" + r1 + "," + r2 ); console.log( r3 ); } // 使用前面定义的工具run(..) run( foo ); //如果p1先决议,那么yield p1就会先恢复执行,然后等待yield p2恢复。如果p2先决 议,它就会耐心保持其决议值等待请求,但是 yield p1 将会先等待,直到 p1 决议。 不管哪种情况,p1 和 p2 都会并发执行,无论完成顺序如何,两者都要全部完成,然后才 会发出 r3 = yield request..Ajax 请求。
生成器委托
function *foo() { console.log( "*foo() starting" ); yield 3; yield 4; console.log( "*foo() finished" ); } function *bar() { yield 1; yield 2; yield *foo(); yield 5; } var it = bar(); it.next().value; // 1 it.next().value; // 2 it.next().value; // *foo() starting // 3 it.next().value; // 4 *foo() finished it.next().value; // 5 //一旦 it 迭代器控制消耗了整个 *foo() 迭代器,it 就会自动转回控制 *bar()。 //yield *暂停了迭代控制,而不是生成器控制。当你调用*foo()生成器 时,现在 yield 委托到了它的迭代器。
yield 可以通过JS闭包的迭代器来模拟实现
// request(..)是一个支持Promise的Ajax工具 function *foo(url) { // 状态1 try { console.log( "requesting:", url ); var TMP1 = request( url ); // 状态2 var val = yield TMP1; console.log( val ); } catch (err) { // 状态3 console.log( "Oops:", err ); return false; } } 转换成es5 function foo(url) { // 管理生成器状态 var state; // 生成器变量范围声明 var val; function process(v) { switch (state) { case 1: console.log( "requesting:", url ); return request( url ); case 2: val = v; console.log( val ); return; case 3: var err = v; console.log( "Oops:", err ); return false; } } // 构造并返回一个生成器 return { next: function(v) { if (!state) { // 初始状态 state = 1; return { done: false, value: process() }; }else if (state == 1) { // yield成功恢复 state = 2; return { done: true, value: process( v ) }; } else { // 生成器已经完成 return { done: true, value: undefined }; } }, "throw": function(e) { // 唯一的显式错误处理在状态1 if (state == 1) { state = 3; return { done: true, value: process( e ) }; }else { throw e; // 否则错误就不会处理,所以只把它抛回 } } }; }
生成器 生成器就是一类特殊的函数,可以一次或多次启动和停止,并不一定非要完成。
首先,传入 6 作为参数 x。然后调用 it.next(),这会启动 foo(..)。 在foo(..)内部,开始执行语句var y = x ..,但随后就遇到了一个yield表达式。它 就会在这一点上暂停 foo(..)(在赋值语句中间!),并在本质上要求调用代码为 yield 表达式提供一个结果值。接下来,调用it.next( 7 ),这一句把值7传回作为被暂停的 yield 表达式的结果。 所以,这时赋值语句实际上就是var y = 6 7。现在,return y返回值42作为调用 it.next( 7 ) 的结果。 注意,这里有一点非常重要。
yield 会导致生成器在执行过程中发送出一个值,这有点类似于中间的 return。
第一个 next(..) 总是启动一个生成器,并运行到第一个 yield 处。不过,是第二个 next(..) 调用完成第一个被暂停的 yield 表达式,第三个 next(..) 调用完成第二个 yield, 以此类推。
yield 并不会阻塞整个程序,它只是暂停或阻塞了生成器本身的代码。
迭代器 迭代器是一个定 义良好的接口,用于从一个生产者一步步得到一系列值。
try catch 如果在生成器内有 try..finally 语句,它将总是运行,即使生成器已经外部结束。
获得 Promise 和生成器最大效用的最自然的方法就是 yield 出来一个 Promise,然后通过这个 Promise 来控制生成器的迭代器。
async 与 await async 函数。最后,我们不再 yield 出 Promise,而是用 await 等待它决议 如果你 await 了一个 Promise,async 函数就会自动获知要做什么,它会暂停这个函数(就 像生成器一样),直到 Promise 决议。
Promise 并发
生成器委托
yield 可以通过JS闭包的迭代器来模拟实现