三、 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)
})
})
})










网友评论