企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## Generator函数 > 1.基本用法 > 2. next方法参数 > 3.for of 循环 > 4.Generator.prototype.throw() > 5.Generator.prototype.return() > 6.yield* 表达式 > 7.作为对象属性的 Generator 函数 > 8.Generator 函数的this > Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同 1.基本用法 形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”) ~~~ function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; } var hw = helloWorldGenerator(); ~~~ ~~~ hw.next() // { value: 'hello', done: false } hw.next() // { value: 'world', done: false } hw.next() // { value: 'ending', done: true } hw.next() // { value: undefined, done: true } ~~~ 第一次调用,Generator 函数开始执行,直到遇到第一个yield表达式为止。next方法返回一个对象,它的value属性就是当前yield表达式的值hello,done属性的值false,表示遍历还没有结束 第二次调用,Generator 函数从上次yield表达式停下的地方,一直执行到下一个yield表达式。next方法返回的对象的value属性就是当前yield表达式的值world,done属性的值false,表示遍历还没有结束 ~~~ function* f() { console.log('执行了!') } var generator = f(); setTimeout(function () { generator.next() }, 2000); ~~~ yield表达式如果用在另一个表达式之中,必须放在圆括号里面 ~~~ function* demo() { console.log('Hello' + yield); // SyntaxError console.log('Hello' + yield 123); // SyntaxError console.log('Hello' + (yield)); // OK console.log('Hello' + (yield 123)); // OK } ~~~ yield表达式用作函数参数或放在赋值表达式的右边,可以不加括号 ~~~ function* demo() { foo(yield 'a', yield 'b'); // OK let input = yield; // OK } ~~~ 2.next 方法的参数 ~~~ function* f() { for(var i = 0; true; i++) { var reset = yield i; if(reset) { i = -1; } } } var g = f(); g.next() // { value: 0, done: false } g.next() // { value: 1, done: false } g.next(true) // { value: 0, done: false } ~~~ ~~~ function* foo(x) { var y = 2 * (yield (x + 1)); var z = yield (y / 3); return (x + y + z); } var a = foo(5); a.next() // Object{value:6, done:false} a.next() // Object{value:NaN, done:false} a.next() // Object{value:NaN, done:true} var b = foo(5); b.next() // { value:6, done:false } b.next(12) // { value:8, done:false } b.next(13) // { value:42, done:true } ~~~ ~~~ function* dataConsumer() { console.log('Started'); console.log(`1. ${yield}`); console.log(`2. ${yield}`); return 'result'; } let genObj = dataConsumer(); genObj.next(); // Started genObj.next('a') // 1. a genObj.next('b') // 2. b ~~~ 与 Iterator 接口的关系 ~~~ var myIterable = {}; myIterable[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; }; [...myIterable] // [1, 2, 3] ~~~ 3.for of 循环 ~~~ function* foo() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6; } for (let v of foo()) { console.log(v); } // 1 2 3 4 5 ~~~ 上面代码使用for...of循环,依次显示 5 个yield表达式的值。这里需要注意,一旦next方法的返回对象的done属性为true,for...of循环就会中止,且不包含该返回对象,所以上面代码的return语句返回的6,不包括在for...of循环之中 除了for...of循环以外,扩展运算符(...)、解构赋值和Array.from方法内部调用的,都是遍历器接口。这意味着,它们都可以将 Generator 函数返回的 Iterator 对象,作为参数 4.Generator.prototype.throw() ~~~ var g = function* () { try { yield; } catch (e) { console.log('内部捕获', e); } }; var i = g(); i.next(); try { i.throw('a'); i.throw('b'); } catch (e) { console.log('外部捕获', e); } // 内部捕获 a // 外部捕获 b ~~~ 6.Generator.prototype.return() ~~~ function* gen() { yield 1; yield 2; yield 3; } var g = gen(); g.next() // { value: 1, done: false } g.return('foo') // { value: "foo", done: true } g.next() // { value: undefined, done: true } ~~~ 8.yield* 表达式 ~~~ function* fool(){ yield 'a'; yield 'b'; } function* bar() { yield 'x'; yield* foo(); yield 'y'; } // 等同于 function* bar() { yield 'x'; yield 'a'; yield 'b'; yield 'y'; } // 等同于 function* bar() { yield 'x'; for (let v of foo()) { yield v; } yield 'y'; } for (let v of bar()){ console.log(v); } // "x" // "a" // "b" // "y" ~~~ 7.作为对象属性的 Generator 函数 ~~~ let obj = { * myGeneratorMethod() { ··· } }; let obj = { myGeneratorMethod: function* () { // ··· } }; ~~~ 8.Generator 函数的this Generator 函数总是返回一个遍历器,ES6 规定这个遍历器是 Generator 函数的实例,也继承了 Generator 函数的prototype对象上的方法。 ~~~ function* g() {} g.prototype.hello = function () { return 'hi!'; }; let obj = g(); obj instanceof g // true obj.hello() // 'hi!' ~~~ ~~~ function* g() { this.a = 11; } let obj = g(); obj.next(); obj.a // undefined ~~~ Generator 函数也不能跟new命令一起用,会报错 ~~~ function* F() { yield this.x = 2; yield this.y = 3; } new F() // TypeError: F is not a constructor ~~~ ~~~ function* F() { this.a = 1; yield this.b = 2; yield this.c = 3; } var f = F.call(F.prototype); f.next(); // Object {value: 2, done: false} f.next(); // Object {value: 3, done: false} f.next(); // Object {value: undefined, done: true} f.a // 1 f.b // 2 f.c // 3 ~~~ 9.应用 Generator 函数的暂停执行的效果,意味着可以把异步操作写在yield表达式里面,等到调用next方法时再往后执行。这实际上等同于不需要写回调函数了,因为异步操作的后续操作可以放在yield表达式下面,反正要等到调用next方法时再执行。所以,Generator 函数的一个重要实际意义就是用来处理异步操作,改写回调函数 * ajax的异步应用 ~~~ function* main() { var result = yield request("http://xxx.com/api"); var resp = JSON.parse(result); console.log(resp.value); } function request(url) { $.ajax(url, function(response){ it.next(response); }); } var it = main(); it.next(); ~~~ * 逐行读取文件 ~~~ function* numbers() { let file = new FileReader("numbers.txt"); try { while(!file.eof) { yield parseInt(file.readLine(), 10); } } finally { file.close(); } } ~~~ ### 课后习题 1.下面代码的运行结果是什么? ~~~ function* gen(x) { var a = yield x+1; var b = yield a +3; return a + b } var g = gen(); console.log(g.next()); console.log(g.next(3)); console.log(g.next(4)); ~~~ ~~~ function* gen(){ yield 1; yield 2; yield 3; return 4; } var g = gen(); for (let s of g) { console.log(s); } ~~~