generator

作者: zdxhxh | 来源:发表于2019-11-05 09:32 被阅读0次

三、 generator函数

Generator (分段函数)是 ES6 提供的一种异步编程解决方案,非常邪门

我们应该把重心放在遍历与配合promise的使用方案上面

1. 定义

function* helloworld() { }

调用方法与普通函数一致

2. yield(产出)

这货用于定义内部状态,你可以理解为[状态1,状态2...]

function* helloworld() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

没有返回值

function* helloworld() {
  let a = yield 'hello';  // undefined
}

只能用于generator函数里面

3. next

next是generator函数执行后返回迭代器的一个方法,类似java中的Iterator的next,是一个指针

没调用一次next,指针会指向数组的下一个索引(yield/return)

[状态1,状态2...,return]
^ -> ^ -> ^

let iterator = helloworld()
iterator.next()   // {value : 'hello',done : false}
iterator.next()   // {value : 'world',done : false}
iterator.next()   // {value : 'undefined',done : true}

采用惰性求值的方式 ,只有当指针移动到下标才会对其求值。估计是模块

next 参数

next可以带参数,该参数等于所在yield的返回值

真的,我想喷这阮一峰的ES6,你这个例子写得不能幼儿园一点么?这么复杂的例子学习这么简单的知识,浪费我的时间

我来给你个幼儿园版本

function* es6() { 
  let a = yield 'a'
  console.log(a)    // logs a 
  yield 'b'
}
let iterator = es6()
const { value } = iterator.next()
iterator.next(value)

第一次输入值是无效的。也没有意义

4. 协程

generator函数不完全实现了协程,即我这个函数可以执行到一半,然后将线程交给另一个函数。

这样做的好处是 : 抛出异常时,可以找到原始的调用栈,如果是普通的异步回调函数,出错后,原始调用栈立刻结束。

缺点 : 多占用内存

5. 遍历

1. 使用Iterator(同步)

对象有Symbol.iterator属性,当它指向*函数时

可以使用扩展运算符遍历*函数

let temp = {} 
temp[Symbol.iterator] = es6 
function* es6() {
  yield 1;
  yield 2;
  yield 3;
}
console.log(...temp)

2. 使用递归(同步)

scheduler(longRunningTask(initialValue));
function scheduler(task) {
  var taskObj = task.next(task.value);
  // 如果Generator函数未结束,就继续调用
  if (!taskObj.done) {
    task.value = taskObj.value
    scheduler(task);
  }
}

3. for.. of (同步)

let iterator = es6()
for (var step of iterator){
  console.log(step);
}

4. 异步遍历器

这个我是无语了,es6这本根本没有给出完整方案,云里云外的。

他那个*map更是扯的离谱,你里面都遍历一个generator函数,遍历器也是一个generator,我这是要定义多少个iterator给你调用next() ?

真的傻逼!!

这里简单分析一下。

我们经常使用promise解决回调地狱,而最常见就是数据库的操作

new Promise(function(resolve,reject){
  Sort.findById({ _id: 'xxx' }, (err, result)=>{
    if(err) reject(err)
    reject(result)
  })
})

但每次都要写上面的这种形式的代码,太浪费生命,所以可以使用下列代码

function promisefy(fn) { 
  return function(...args) { 
    return new Promise((resolve,reject)=> {
      fn(...args,(err,data)=> { 
        if(err) reject(err)
        resolve(data)
      })
    })
  }
}

这样我们就完成了一个通用的promise化函数。

const findById = promisefy(Sort.findById)
const data = await findById({ _id: 'xxx'})

函数异步化,是指我希望在yield后面跟一个promise*

function* es6(){
    let r1 = yield read('1.txt','utf8')
    let r2 = yield read(r1,'utf8')
    let r3 = yield read(r2,'utf8')
    return r3
}
let iterator = es6()
let {value,done} = iterator 
value.then(data=> { 
  let { value,done } = it.next()
  return value 
}).then(data=> { 
  let { value,done } = it.next()
   return value
}).then(data=> { 
  let { value,done } = it.next()
  return value
})

这样,显得非常繁琐。

我们使用co

co(r()).then(data=>{
    console.log(data) // 打印输出 最后return的值
})

这是因为

function co(it) {
  return new Promise((resolve, reject) => {
    function next(data) {
      let { value, done } = it.next(data)
      if (!done) {
        value.then((data) => {
          next(data)
        }, reject)
      } else {
        resolve(value)
      }
    }
    next()
  })
}

5. 分析 : async + await = generator + co?

export function api(data) { 
  return fetch({
    url : 'xxx/xxx',
    method : 'post'
  })
}

methods : { 
  async getXXX() { 
    const res = await api()
    console.log(res)
    const res2 = await api()
    console.log(res2)
  }
}
methods : { 
  getXXX : function* () { 
    const res = yield api()
    console.log(res)
    const res2 = yield api()
    console.log(res2)
  }
}
const it = getXXX()
co(it).then(res=>{
  console.log(res)  // 打印输出 最后return的值
})

事实上,async与await编译后确实为generator函数,这点就不深究了

从co的内部代码执行来看,每当我执行一次yield,

new Promise(function excutor(resolve) { 
  const promise1 = api()
  // done = false 
  promise2 = promise1.then(value=> { 
    {
      const res = value 
      console.log(res)

      const promise3 = api()

      promise3.then(value=>{
        const res2 = value 
        console.log(res2)

        const { value ,done } = return语句
        resolve(value)
      })
    }
  })
})

在async/await代码内部也是如此.

6. 与promise的区别

传统的promise.then调用是先创建所有由then生成的promise,并利用闭包将async函数推入上一个promise实例的异步回调数组中,最后通过触发第一个promise的resolve触发之后所有promise的resolve。详情可以看

而co是等待第一个异步执行完后,才通过it.next()获取下一个promise,才通过then生成下下个promise,并在下下个promise 的 excutor传入回调。

co执行过程

传统Promise链调用

// 回调函数加载阶段
p1 { 
  excutor : asyncTask,
  asyncArr : [] 
}
p2 = p1.then(cb1) {
  excutor : p1.asyncArr.push(cb1),
  asyncArr : []
}
p3 = p2.then(cb2) { 
  excutor : p2.asyncArr.push(cb2),
  asyncArr : []
}

// 异步执行resolve阶段
value1 = asyncTask.end
p1.resolve(value1)
value2= cb1(value1)
p2.resolve(cbValue)
value3 = cb3(value2)
p3.resolve(value3)
// p3回调队列是空的
new Promise(excutor())
.then(value=> {

})
.then(value=> { 

})
.then(value=> { 

})

以下这个图更直观了显示了co的运行过程

new Promise(excutor(resolve) {  
  const promise1 = api() 
  promise1.then(value=> { 
    const promise2 = api2()
    promise2.then(value=> { 
      const { done } = trun 
      resolve(value)
    })
  })
}) 

相关文章

网友评论

      本文标题:generator

      本文链接:https://www.haomeiwen.com/subject/yhqsbctx.html