迭代器是一种特殊的对象
特点:1)对象都有next()方法;2)调用next方法后返回一个对象;3)返回的对象有done和value属性,done为boolon类型,value为下一个将要返回的值
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, 4])
console.log(iterator.next()) //1
console.log(iterator.next()) //2
console.log(iterator.next()) //3
console.log(iterator.next()) //4
生成器:一种返回迭代器的函数
function* createIterator_() {
yield 1
yield 2
yield 3
}
let iterator1 = createIterator_()
console.log(iterator1.next().value)
console.log(iterator1.next().value)
console.log(iterator1.next().value)
console.log(iterator1.next().value) //undefined
迭代消息传递
- next()方法返回的对象中的value值为生成器中yield语句返回的值
- next()方法中若传入值,那么该值会改变上一次yield语句的返回值
- next()与yield语句不匹配,N个yield语句对应N+1次next()方法调用
function* createIterator() {
//next 内部传入的值会替代上一次yield的返回值 但是首次传入的时候
//并没有-1次传来的值 因此 无论传递什么都会被丢弃
let first = yield 1
// 上一次返回的值是 1 赋值给first
let second = yield first + 2
// 上一次返回的值是 first + 2 赋值给second
let third = yield second + 3
}
let ex1 = createIterator()
//第一次无论传多少值都被丢弃
console.log(ex1.next(8)) //1
console.log(ex1.next(4)) //6
console.log(ex1.next(5)) //8
console.log(ex1.next(6)) //undefined, N+1次调用next()
迭代器模式的异步任务
示例:生成器在yield处暂停,本质上是提出一个问题:“我该返回什么值来赋给变量text”
在本示例中,我们把异步函数foo抽象了出去,在生成器中忽略了异步的细节,让我们以一种同步的形式来追踪整个流程:“1. foo函数发起网络请求; 2.获得返回的数据再处理”
function foo(x, y){
ajax("http://sample.url/?x=" + x + "&y=" + y,
function(err, data){
if(err){
it.throw(err)
}else{
//利用next改变上一次yiled的返回值
it.next(data)
}
})
}
function *main(){
try{
const text = yield foo(11, 31)//it.next(data)改变了yield表达式返回值
console.log(text)
}catch(err){
console.log(err)
}
}
let it = main()
it.next()//首次启动
此外,还有上述man方法中利用try...catch可以捕获异步函数调用过程中产生的错误。 这在promise中是无法实现的。
function *main(){
let x = yield "Hello world"
yield x.toLowerCase()
}
let it = main()
it.next().value//Hello world
//版本一:捕获yield表达式运行错误
try{
it.next(42)// x.toLowerCase() 触发了异常
}catch(err){
console.log(err)//此处捕获了错误
}
//版本二:手动抛出错误
try{
it.throw('err')
}catch(err){
console.log(err)//err
}
终极版本
纯生成器
function run(generate) {
let task = generate()
//在这里task第一次迭代
let result = task.next()
function step() {
if (!result.done) {
const value = result.value
if (typeof value == 'function') {
//这里传入的是callback
value((err, data) => {
if (err) {
result = task.throw(err)
return
} else {
result = task.next(data)
step()
}
})
} else {
result = task.next(value)
}
}
}
//step函数第一次运行的时候task第二次迭代
step()
}
//使用范例
const readFile = filename =>{
return callback =>{
//currying,callback在迭代器中传入
fs.readFile(filename, callback)
}
}
run(function* (){
let contents = yield readFile("example.txt")//yield右边是第一次迭代的结果 迭代器对象 value是函数
console.log(contents) //此处ontents变成了第二次迭代过程中next(data)中的data
})
迭代器+Promise
实例1:thunk
function run(generate) {
let task = generate()
let result = task.next()
function step() {
if (!result.done) {
//promise实例中this.data = result.value
let promise = Promise.resolve(reslut.value)
promise.then(value => {
result = task.next(value)
step()
}.catch(error=>{
result = task.throw(error)
step()
})
}
}
step()
}
//使用范例
const readFile = filename =>{
return new Promise((resolve, reject)=>{
fs.readFile(filename, (err, data)=>{
if(err){
reject(err)
}else{
resolve(data)
}
})
})
}
run(function* (){
try{
let contents = yield readFile("example.txt")
console.log(contents)
}catch(err){
console.log(err)
}
})
实例2:
个人更喜欢第一个例子,更容易理解,而且不管怎么都要将生成器内部的异步函数抽象出来。
function run(gen) {
let args = [].slice.call(arguments, 1)
let it = gen.apply(this, args)
return Promise.resolve().then(function handleNext(value) {
//第一次迭代next()方法的时候 value为undefined,第一次调用不过无所谓什么值
var next = it.next(value)
return (function handleResult(next) {
//当迭代器出现错误的时候 done会变成true
if (next.done) {
//返回一个值
return next.value
} else {
//在此处巧妙地next.value传入回调函数handleNext,
return Promise.resolve(next.value)
.then(handleNext, function handleErr(err) {
//如果运行出错,就把错误传回生成器
return Promise.resolve(it.throw(err))
.then(handleResult) //handleResult(it.throw(err))
})
}
})(next)
})
}