生成器函数

  • generator(生成器)是ES6标准引入的新的数据类型。我们可以认为是它是一个批量制造 函数 的工厂, 在实际应用中常用于处理异步操作,但究其用法过于繁琐,在ES2017后用更为简便的async await来替代了生成器函数用于处理异步操作! 但是这篇博客讲到的Redux-Saga是基于生成器函数来实现的,因此需要讲解一下它(生成器函数)的基本使用

基本使用:

  1. 生成器函数
/* ---------------------------------- 生成器函数 ----------------------------------------- */
/*
可以配合 yield 来使用, 暂停或者回复函数的执行, 有点类似 return
但是又不完全一致, yield 可以让函数反复执行,当然在生成器函数里面
也是可以使用 return 的,不过他会直接终止函数的运行罢了!
*/
// 1. 创建生成器函数(函数名前面 + *)
function * count(){
let arr = [1,2,3,4,5]// 创建一个数组
// 在循环遍历中返回该数组的每一个数据
for(var i = 0; i < arr.length; i++){
yield arr[i] // 返回一个arr中的数据
}
}

// 2. 使用生成器
const c = count()

// 3. 获取数组数据
console.log(c.next());// { value: 1, done: false } 函数未执行完毕(done: false)
console.log(c.next());// { value: 2, done: false }
console.log(c.next());// { value: 3, done: false }
console.log(c.next());// { value: 4, done: false }
console.log(c.next());// { value: 5, done: false }
console.log(c.next());// { value: undefined, done: true } 函数执行完毕(done: true)

/*
由此可见,每一次调用生成器的next方法就会使得生成器函数内部的yield执行且只执行一次,
返回一个对象,对象里面的value属性就是数组中对应执行步骤的值,随着我们不断的调用next
方法,for循环遍历结束时,最后一次返回的对象中的done属性就会变成 true 表示 该生成器
函数执行完毕,由此可见我们可以将其使用在异步操作中,在项目中请求数据的时候,等待数据
请求完毕后调用next方法执行下一步的操作! (生成器函数中有n个 yield 就 n个执行步骤)
*/

结果展示:

image

  1. next()函数的基本使用
/* ----------------------- 讲解一下 next() 方法 ------------------------- */
/*
之前我们知道next方法返回一个对象,可以通过value属性得到返回值,实际上
next方法也可以接收一个值;
*/

// 1. 创建一个生成器函数
function * test(){
console.log('生成器函数第一次执行');
// 使用 yield 暂停生成器函数的执行
var input1 = yield '第一次返回的数据';
console.log('生成器函数第二次执行',input1);
// 使用 yield 暂停生成器函数的执行
var input2 = yield '第二次返回的数据';
console.log('生成器函数第三次执行',input2);
// 使用 yield 暂停生成器函数的执行
var input3 = yield '第三次返回的数据';
console.log('生成器函数第四次执行',input3);
}

// 2. 使用生成器函数
const Test = test()

// 3. 使用next调用生成器函数
console.log(Test.next(0));
console.log(Test.next(1));
console.log(Test.next(2));
console.log(Test.next(3));

/*
由此可见,我们可以使用 next方法来控制 生成器函数的执行和暂停以及获取每一次
暂停后的返回值, 我们同样可以在next()里面传入参数,该参数能被 yield 捕获到,
并且从结果中我们可以看出,第一个next传递参数并不会被 yield 所捕获,因为
规范和所有兼容浏览器都会默默丢弃传递给第一个next()的任何东西, 因此我们可以将
第一个next()看成是启动生成器函数, 只有从第二个next开始传递的参数才会被 yield
所捕获!
*/

结果展示:

image

  1. 使用生成器函数处理异步操作
/* ------------ 使用生成器函数结合promise处理异步操作 ------------ */
// 1. 封装函数模拟网络请求
function requestDate(url) {
return new Promise((resolve, reject) => {
setTimeout(() => { // 使用延时1s表示请求时间
resolve(url)
}, 1000)
})
}

// 2. 创建生成器函数
function * getData() {
const res1 = yield requestDate("第一次请求回来的数据")
console.log("res1:", res1) // 第一次请求回来的数据
const res2 = yield requestDate(res1 + ",第二次请求回来的数据")
console.log("res2:", res2) // 第一次请求回来的数据,第二次请求回来的数据
const res3 = yield requestDate(res2 + ",第三次请求回来的数据")
console.log("res3:", res3) // 第一次请求回来的数据,第二次请求回来的数据,第三次请求回来的数据
}

// 3. 使用生成器函数
const generator = getData()

/*
因为生成器的next方法返回的对象中的value值是返回的Promise
我们可以通过Promise的then拿到第一次网络请求的结果res1
*/
generator.next().value.then(res1 => {
// 将第一次网络请求的结果传入生成器函数时, 可以拿到第二次网络请求的结果
generator.next(res1).value.then(res2 => {
// 将第二次网络请求的结果传入生成器函数, 可以拿到第三次网络请求的结果
generator.next(res2).value.then(res3 => {
// 将第三次网络请求的结果传入生成器函数
generator.next(res3)
})
})
})

/*
或者写一个递归函数来执行,判断next返回对象中的done属性是否为 true
*/
// 2.定义一个递归函数(自动执行)
function exec(res) {
const result = generator.next(res)
/* 通过done属性来判断生成器函数是否执行完毕 */
if (result.done) return result.value
result.value.then(res => {
exec(res)
})
}

// 执行递归函数
// exec()// 结果与行面的链式调用一致

结果展示:

image