前言
在此文之前默认你已经知道了 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 那样一层层回调嵌套。