《你不知道的javascript》读书笔记—生成器

生成器函数

生成器函数是一个特殊的函数, 它依旧是一个函数,基本的函数特性都是没有改变的。展示生成器的执行模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 function *foo(x) {
var y = x + (yield "Hello"); // <-- yield一个值!
console.log('y: '+y);//13
y = y +(yield y);
console.log(y);// 0
return y;//生成器函数带了return语句,最后一个next()的value就会有值(返回的值),如果没有,调用默认的return //语句,return undefined。
}
var it = foo( 6 );
console.log(it.next().value); // 第一个next(), 并不传入任何东西; "Hello"
res = it.next( 7 ); // 向等待的yield传入7
console.log(res.value); // 13
console.log(it.next(-13));// { value: 0, done: true }

//yield和next同用的时候,next()会比yield的次数多一次,最后一次next()会由return语句回答
//如果生成器函数没有return语句,用默认的return语句,return undefined。

迭代器

  • 概念

    • 可迭代:迭代器对象,可用的方法:Symbol.iterator

    • 调用next()接收到done:true停止

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      var something = (function(){
      var nextVal ;
      return {
      [Symbol.iterator]:function(){
      return this;
      },
      next:function(){
      if(nextVal === undefined){
      nextVal = 1;
      }
      else{
      nextVal = 3*nextVal +6;
      }
      return {done:false,value:nextVal};
      }
      };
      })();
      something.next().value;//1
      something.next().value;//9
      //遍历迭代器对象
      for (var v of something) {//something是一个迭代器对象,迭代器对象可用for...of..去循环
      console.log( v );//1 9 33
      // 不要死循环!
      if (v > 100) {
      break;
      }
      }
      //数组用迭代器遍历
      var a = [1,3,5,7,9];
      for (var v of a) {
      console.log( v );
      }// 1 3 5 7 9
      或者
      var a = [1,3,5,7,9];
      var it = a[Symbol.iterator]();
      it.next().value; // 1

      //生成器迭代器
      function *something() {
      var nextVal;
      while (true) {
      if (nextVal === undefined) {
      nextVal = 1;
      } else {
      nextVal = (3 * nextVal) + 6;
      }
      yield nextVal;
      }
      }
      //迭代器遍历
      for(var v of something()){//something()是指生成器函数执行就生成了迭代器
      console.log(v);
      if(v>100){
      break
      }
      }

  • 异步迭代生成器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    //同步实现异步处理方式
    function foo(x,y) {
    ajax("http://some.url.1/?x=" + x + "&y=" + y,
    function(err,data){
    if (err) {
    // 向*main()抛出一个错误
    it.throw( err );
    }else {
    // 用收到的data恢复*main()
    it.next( data );
    }
    });
    }
    function *main() {
    try {
    var text = yield foo( 11, 31 );//由于foo()函数没有返回值,第一个next()返回的是undefined
    console.log( text );
    }catch (err) {
    console.error( err );
    }
    }
    var it = main();
    // 这里启动!
    it.next();

    //生成器+Promise
    function foo(x,y) {
    return request(
    "http://some.url.1/?x=" + x + "&y=" + y
    );
    }
    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;//foo()函数返回的值
    // 等待promise p决议
    p.then(
    function(text){
    it.next( text );
    },
    function(err){
    it.throw( err );
    }
    );

    //promise驱动的生成器 ==>替代上面生成器+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);
    } );
    }
    run(main);
  • 生成器委托

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    //场景:异步请求并发 采用了yield *机制
    function *foo() {
    var r2 = yield request( "http://some.url.2" );
    var r3 = yield request( "http://some.url.3/?v=" + r2 );
    return r3;
    }
    function *bar() {
    var r1 = yield request( "http://some.url.1" );
    // 通过 yeild* "委托"给*foo()
    var r3 = yield *foo();//foo()执行得到生成器
    console.log( r3 );
    }
    run( bar );

    //递归委托
    function *foo(val) {
    if (val > 1) {
    // 生成器递归
    val = yield *foo( val - 1 );
    }
    return yield request( "http://some.url/?v=" + val );
    }
    function *bar() {
    var r1 = yield *foo( 3 );
    console.log( r1 );
    }
    run( bar );

  • 生成器并发

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    //异步请求并发协调
    // request(..)是一个支持Promise的Ajax工具
    var res = [];
    function *reqData(url) {
    var data = yield request( url );
    // 控制转移
    yield;
    res.push( data );
    }
    var it1 = reqData( "http://some.url.1" );
    var it2 = reqData( "http://some.url.2" );
    var p1 = it.next();
    var p2 = it.next();
    p1.then( function(data){
    it1.next( data );
    } );
    p2.then( function(data){
    it2.next( data );
    } );
    Promise.all( [p1,p2] )
    .then( function(){
    it1.next();
    it2.next();
    } );

    //==>改进方案
    // request(..)是一个支持Promise的Ajax工具
    runAll(
    function*(data){
    data.res = [];
    // 控制转移(以及消息传递)
    var url1 = yield "http://some.url.2";
    var p1 = request( url1 ); // "http://some.url.1"
    // 控制转移
    yield;
    data.res.push( yield p1 );
    },
    function*(data){
    // 控制转移(以及消息传递)
    var url2 = yield "http://some.url.1";
    var p2 = request( url2 ); // "http://some.url.2"
    // 控制转移
    yield;
    data.res.push( yield p2 );
    }
    );

Thunk.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//thunkify(..) 的实现接收 foo(..) 函数引用以及它需要的任意参数, 并返回 thunk 本身(fooThunk(..) ) 
function foo(x,y,cb) {
setTimeout( function(){
cb( x + y );
}, 1000 );
}

function thunkify(fn) {
var args = [].slice.call( arguments, 1 );
console.log(args)
return function(cb) {
args.push( cb );
return fn.apply( null, args );
};
}
var fooThunk = thunkify( foo, 3, 4 );
// 将来
fooThunk( function(sum) {
console.log( sum ); // 7
} );

//改进的方案--js典型方案
function thunkify(fn) {
return function() {
var args = [].slice.call( arguments );
return function(cb) {
args.push( cb );
return fn.apply( null, args );
};
};
}


//Promise与thunk.js的比较
// 对称: 构造问题提问者
var fooThunkory = thunkify( foo );
var fooPromisory = promisify( foo );
// 对称: 提问
var fooThunk = fooThunkory( 3, 4 );
var fooPromise = fooPromisory( 3, 4 );
// 得到答案
fooThunk( function(err,sum){
if (err) {
console.error( err );
}else {
console.log( sum ); // 7
}
} );
// 得到promise答案
fooPromise.then(
function(sum){
console.log( sum ); // 7
},
function(err){
console.error( err );
}
);


ES6之前的生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// request(..)是一个支持Promise的Ajax工具
function *foo(url) {
try {
console.log( "requesting:", url );
var val = yield request( url );
console.log( val );
}catch (err) {
console.log( "Oops:", err );
return false;
}
}
var it = foo( "http://some.url.1" );



//简单构造类似Promise
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()
};
}// yield成功恢复
else if (state == 1) {
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;
}
}
};
}

本文由 Abert 创作,采用 知识共享署名 4.0 国际许可协议。

本站文章除注明转载/出处外,均为本站原创或翻译,转载请务必署名。