Generator函数
迭代器
有next办法,执行返回后果对象({value:'XX', done:'XX'})
// ES5实现迭代器function createIterator(items) { var i = 0; return { next: function() { var done = i >= items.length; var value = !done ? items[i++] : undefined; return { done: done, value: value }; } };}var iterator = createIterator([1, 2, 3]);iterator.next(); // {value:1,done:false}iterator.next(); // {value:2,done:false}iterator.next(); // {value:3,done:false}iterator.next(); // {value:undefined,done:true}
可迭代协定
[Symbol.iterator]属性内置可迭代对象 String Array Map Set等
迭代器协定
有next办法,执行后返回一个对象{value:'XX', done:'XX'}
生成器
Generator函数(生成器)
ES6异步编程解决方案,在执行处暂停,又能从暂停处继续执行
- 申明:通过function*申明
- 返回值:合乎可迭代协定和迭代器协定的生成器对象
yield
- 只能呈现在Generator函数
- 用来暂停和复原生成器函数
生成器对象
生成器对象原型上有三个办法
- next(param)
- return(param)
- throw(param)
next
- 执行
遇yield暂停,将紧跟yield表达式的值作为返回的对象的value
没有yield,始终执行到return,将return的值作为返回的对象的value
没有return,将undefined作为返回的对象的value - 参数
next办法能够带一个参数,该参数会被当做上一个yield表达式的返回值
代码剖析:
function* createIterator() { let first = yield 1; let second = yield first + 2; yield second + 3;}let iterator = createIterator();iterator.next(); // {value:1, done:false}iterator.next(4); // {value: 6, done: false}iterator.next(5); // {value: 8, done: false}iterator.next(); // {value: undefined, done: true}
执行过程:
- 遇到yield暂停,返回yield表达式后的1作为返回对象的value,执行还未完结,后果为{value:1, done:false}
- 因为第一步执行到yield就曾经暂停了,first还没有被赋值。第二次next执行的时候,first才开始赋值,因为传入了4,替换掉第一步中的1赋值给了first,first是4。返回yield后表达式计算的后果6作为返回对象的value,所以后果为{value: 6, done: false}
- 同第二步的过程,yield后表达式计算结果为8,尽管前面没有代码了,然而相当于有一个默认的return undefined,所以还没有完结,因而返回{value: 8, done: false}
- return undefined,代码执行完结,后果为{value: undefined, done: true}
return(param)
给定param值终结遍历器,param默认为undefined
function* createIterator() { yield 1; yield 2; yield 3;}let iterator = createIterator();iterator.next(); // {value:1, done:false}iterator.return(); // {value:undefined, done:true}iterator.next(); // {value:undefined, done:true}
throw(param)
让生成器对象外部抛出谬误
function* createIterator() { let first = yield 1; let second; try { second = yield first + 2; } catch (e) { second = 6; } yield second + 3;}let iterator = createIterator();iterator.next(); // {value:1, done:false}iterator.next(10); // {value:12, done:false}iterator.throw(new Error("error")); // {value:9, done:false}iterator.next(); // {value:undefined, done:true}
- 遇到yield暂停,返回yield表达式后的1作为返回对象的value,执行还未完结,后果为{value:1, done:false}
- 返回{value:12, done:false}
- iterator.throw(new Error("error"))会进入try{}catch{}中的catch,second赋值为6,持续往下,遇到yield才会进行,返回{value:9, done:false}
- 返回{value:undefined, done:true}
yield* 生成器函数/可迭代对象
- 委托给其余可迭代对象
- 作用:复用生成器
代码剖析
function* generator1() { yield 1; yield 2;}function* generator2() { yield 100; yield* generator1(); // 控制权交出,进入generator1 yield 200;}let g2 = generator2();g2.next(); // {value:100, done:false}g2.next(); // {value:1, done:false}g2.next(); // {value:2, done:false}g2.next(); // {value:200, done:false}g2.next(); // {value:undefined, done:true}
Generator函数的实现原理
协程
- 一个线程存在多个协程,但同时只能执行一个
- Generator函数时协程在ES6的实现
- yield挂起一个协程(交给其余协程),next唤醒协程
Generator函数利用
需要:依照程序打印文件
间接用回调实现须要多层嵌套
应用Generator函数实现如下:
function* readFilesByGenerator() { const fs = require("fs"); const files = [ "/Users/kitty/testgenerator/1.json", "/Users/kitty/testgenerator/2.json", "/Users/kitty/testgenerator/3.json" ]; let fileStr = ""; function readFile(filename) { fs.readFile(filename, function(err, data) { console.log(data.toString()); f.next(data.toString()); }); } yield readFile(files[0]); yield readFile(files[1]); yield readFile(files[2]);}// 调用const f = readFilesByGenerator();f.next()
然而此种办法在readFilesByGenerator函数外部用到了在里面定义的生成器对象f,耦合度太高。
thunk函数
求值策略
传值调用
- 以sum(x+1,x+2)为例,先计算x+1和x+2的值,再传入sum函数进行计算
传名调用
- 以sum(x+1,x+2)为例,等到sum函数中用到传入的参数时,再去计算x+1和x+2的值
- thunk函数是传名调用的实现形式之一
- 能够实现主动执行Generator函数
const fs = require("fs");const Thunk = function(fn) { return function(...args) { return function(callback) { return fn.call(this, ...args, callback); }; };};const readFileThunk = Thunk(fs.readFile);function run(fn) { var gen = fn(); function next(err, data) { var result = gen.next(data); if (result.done) return; result.value(next); } next();}const g = function*() { const s1 = yield readFileThunk("/Users/kitty/testgenerator/1.json"); console.log(s1.toString()); const s2 = yield readFileThunk("/Users/kitty/testgenerator/2.json"); console.log(s2.toString()); const s3 = yield readFileThunk("/Users/kitty/testgenerator/3.json"); console.log(s3.toString());};run(g);