## 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);
}
~~~
- Less
- 課程規劃
- Less概述
- 變量
- 混合
- 嵌套
- 繼承
- 導入
- 函數
- 其他
- 實戰
- ES6
- 課程規劃
- ES6概述
- let和const命令
- 變量的解構賦值
- 字符串擴展
- 函數擴展
- 數組擴展
- Set和Map數據結構
- Symbol
- Generator 函數
- Promise對象
- Class語法
- Module 的語法
- ES7和ES8
- 實戰
- VUE
- 課程規劃
- vue概述
- vue實例
- 模版語法
- 計算屬性和偵聽器
- Class和Style的綁定
- 條件渲染
- 列表渲染
- 事件處理
- 表單輸入綁定
- 組件基礎
- 過渡和動畫
- 自定義指令
- 過濾器
- 響應式原理
- 實戰課程
- Node
- 課程規劃
- 課程概述
- node入門實例
- 模塊系統
- 回調函數
- 全局對象
- 常用模塊介紹
- 常用模塊介紹-1
- 常用模塊介紹-2
- 常用模塊介紹-3
- npm使用
- express的使用
- express的使用-1
- webpack基礎
- 實戰
- 微信小程序
- 課程規劃
- 課程概述
- 基本配置和生命周期
- wxml模版
- wxss
- wxs
- 組件
- 微信API
- 自定義組件開發
- 實戰小程序
- Element
- 課程規劃
- 課程概述
- 特性介紹
- 組件介紹-基礎組件
- 組件介紹-表單組件
- 組件介紹-數據展示組件
- 組件介紹-提示組件
- 組件介紹-導航組件
- 組件介紹-其他組件
- 綜合案例