最近在学习redux-saga,由于redux-saga需要使用Generator函数,所以下来就回顾了一下Generator
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同
写法上Generator函数与普通函数没什么区别,只是在function
关键字后面多了一个*
号,函数内部多了一个yield
关键字
function* demo() {
console.log(1)
yield 111
console.log(2)
yield 222
console.log(3)
return 333
}
上面的函数即为一个Generator函数,Generator调用后并不会直接执行,而是返回一个指向内部状态的指针对象需要调用该指针对象的next
方法才会向下执行,当遇到yield
关键字的时候,函数会挂起,交给外部,直到遇到下一次调用next
方法
let g = demo()
此时若我们第一次调用
let val1 = g.next()
// 打印 1,val1 = {value: 111, done: false}
let val2 = g.next()
// 打印 2,val2 = {value: 222, done: false}
let val3 = g.next()
// 打印 3,val3 = {value: 333, done: true}
根据结果我们可以很清楚的看到当返回值的done
为true
的时候就代表Generator执行完毕,此外Generator函数还可以被循环
for (let val of demo()) {
console.log(val)
}
// val依次会打印出 111、222,注意当done为true的时候不会打印value的值
当然Generator在工作中最常用到的就是处理异步函数,下面举个例子:
function demo(a, b) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(a + b)
}, 1000)
})
}
function* gen(val) {
let a = yield demo(1, 2)
console.log(a)
let b = yield \'123\'
return b
}
这里需要注意的是,变量a
的值是需要next
方法传入的
let g = gen()
let {value} = g.next()
此时函数中的变量a的值就是再次调用next
传入的值, 比如g.next(value)
,则此时a的值就是一个Promise的实例,显然这不是我们想要的,我们期望拿到的是Promise.resolve
的值
那么根据上面的结果我们可以编写一个Generator执行器
function execGen(gen) {
let g = gen()
function deep(val) {
let {value, done} = g.next(val)
if (done) {
return
}
if (value instanceof Promise) {
value.then(data => {
deep(data)
})
} else {
deep(value)
}
}
deep()
}
execGen(gen)
// gen函数的console会正确的打印出值3
其实如果不是redux-saga的话我感觉我工作中基本不会用到Generator函数,个人感觉更优雅的是async/await 来处理异步情况
async function handler() {
try{
let val = await demo()
}catch (e) {
}
}
最后还是期望有大佬能够帮忙指出相比于async/await,Generator有什么优势^_^