Es6 generator

Posted by Kerr on Tue, Jun 5, 2018

前言

在此文之前默认你已经知道了 ES 是什么。如果不知道请移步

Generator 语法糖

Generator 可以理解为一个状态机,里面管理着多个状态,然后通过迭代器,触发这些状态的变化。它需要配合 yield 关键字使用。由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。

例1 定义

首选看如何定义一个Generator:

 1function * n () {
 2 yield 1;
 3 yield 2;
 4 yield 3;
 5}
 6var g = n()
 7n.next() 
 8n.next()
 9n.next()
10n.next()
11
12输出:
13{value: 1, done: false}
14{value: 2, done: false}
15{value: 3, done: false}
16{value: undefined, done: true}

上面代码可以看出在 function 和 函数名之间加上* 就表示这是一个Generator, 需要用 next() 触发暂停执行的函数,有点像开发中的 debug,点一下走一步,返回值是一个迭代器,done 的状态表示所有状态是否已经遍历完了。 yield 后面可以跟表达式或函数。

例1 返回值
 1function * n () {
 2		   var x =  yield 1;
 3		   console.log("x="+x)
 4		   var y =  yield 2;
 5		   console.log("y="+y)
 6		   var z = yield 3;
 7		   console.log("z="+z)
 8		}
 9
10var x  = n()
11console.log(x.next())
12console.log(x.next(2))
13console.log(x.next(3))
14console.log(x.next(4))
15
16输出:
17{value: 1, done: false}
18x=2
19{value: 2, done: false}
20y=3
21{value: 3, done: false}
22z=4
23{value: undefined, done: true}

由于next方法的参数表示上一个yield表达式的返回值,所以在第一次使用next方法时,传递参数是无效的。V8 引擎直接忽略第一次使用next方法时的参数,只有从第二次使用next方法开始,参数才是有效的。从语义上讲,第一个next方法用来启动遍历器对象,所以不用带有参数。

例3 异步处理
 1
 2function run(fn) {
 3  var gen = fn();
 4
 5  function next(err, data) {
 6    var result = gen.next(data);
 7    if (result.done) return;
 8    result.value(next);
 9  }
10
11  next();
12}
13
14var Thunk = function (fileName) {
15  return function (callback) {
16    return fs.readFile(fileName, callback);
17  };
18};
19
20var readFileThunk = Thunk(fileName);
21readFileThunk(callback);
22
23var g = function* (){
24  var f1 = yield readFileThunk('fileA');
25  var f2 = yield readFileThunk('fileB');
26  // ...
27  var fn = yield readFileThunk('fileN');
28};
29
30run(g);

yield 后面如果是异步处理的那么就需要这个调用函数必须是Thunk 函数。能够让Generator自动迭代下去。

实例应用

下面代码是dva model 的语法。

1
2*list({payload},{call, put}){
3        const response = yield call(func, payload);
4        yield put({
5          type: 'listData',
6          payload: response,
7        });
8 }

首先用yield call( )去异步请求一个 API, 当有返回结果后会执行 put 操作, 所以采用状态租塞方式来实现数据流的处理。而不用像 jquery 那样一层层回调嵌套。