一、迭代器和生成器
1、迭代器iterator
1.1、为什么需要迭代器
1.2、什么是迭代
迭代器从一个数据集合中按照一定的顺序,不断的取出数据的过程
Iterator的作用
-
为各种数据结构,提供一个统一的接口
-
使得数据结构的成员能够按某种次序排列
-
迭代强调是依次取出,不能确定取出的有多少,也不能保证把数据全部取完
迭代和遍历的区别
-
迭代强调的依次取出,不能确定可以取出的值有多少,也不能保证去吧数据全部取完
-
遍历必须保证数据的长度,循环不断的全部取出,针对于数据量过大的情况下使用遍历需要时间过长
2.2、基本语法
迭代器是一个特殊的对象,每一个迭代器对象身上都有一个 next()
方法;调用 next()
会返回一个对象,该对象中包含 value
属性和 done
属性
我们用 ES5 的代码来模拟创建一个迭代器:
const colors = ['red', 'yellow', 'green', 'blue'];
function createIterator(items) {
let i = 0;
// 返回一个迭代器对象
return {
next: function () {
if (i < items.length) {
return { value: items[i++], done: false }
}
return { value: undefined, done: true }
}
}
}
const iterator = createIterator(colors);
let result = iterator.next(); // { value, done }
// 只要 done 属性的值为 false,就表示循环还未结束
while (!result.done) {
console.log(result.value);
result = iterator.next();
}
分析Iterator的遍历过程:
-
创建一个对象,指向当前数据结构的起始位置
-
随后通过 next 方法进行向下迭代指向下一个位置, next 方法会返回当前位置的对象, 对象包含了 value 和 done 两个属性, value 是当前属性的值, done 用于判断是否遍历结束
-
当done为true时,迭代结束
2.3、在类中添加迭代器
在es6中,如果对象具有知名符号的属性 Symbol.iterator,则表示对象可以迭代
ES6 的有些数据结构原生具备 Iterator 接口(比如数组),即不用任何处理,就可以被for…of循环遍历:
let items=['red','yellow','blue','green']
const iterator=items[Symbol.iterator]()
const it1=iterator.next()
console.log(it1); //{ value: 'red', done: false }
const it2=iterator.next()
console.log(it2); //{ value: 'yellow', done: false }
const it3=iterator.next()
console.log(it3); //{ value: 'blue', done: false }
const it4=iterator.next()
console.log(it4); //{ value: 'green', done: false }
const it5=iterator.next()
console.log(it5); //{ value: undefined, done: true }
自定义可迭代对象:
let person={
name:'Giles',
age:23,
[Symbol.iterator]:function(){
let keys=Object.keys(this).filter(item=>item!='values')
//定义index为下标
let index=0
return{
next:()=>{
//获取对象的属性名
let proName=keys[index]
let proval=this[proName]
let result={
value:{proName,proval},
done:true
}
index++
return result
}
}
}
}
for(let item of person){
console.log(item);
}
2.4、调用迭代器的场景
迭代器的使用场景还是蛮多的,解构赋值、扩展运算符、Generator函数、yield*, 下方会简单的列举出来
(1)、 对数组或者集合的解构赋值
let myset=new Set()
myset.add('Giles')
myset.add('Monica')
myset.add('Lucy')
myset.add('Lily')
myset.add('Jerry')
const [first,second,...others]=myset;
console.log(first,second,others);
(2)、扩展运算符
let ary="HelloGiles"
console.log([...ary]);
(3)、 在Generator函数的 yield * 中使用
稍后会详细的介绍 Generator 函数
2、生成器 generator
generator 是 ES6 中新增的一个异步解决方案。
生成器 generator 是一个函数,该函数的返回值是一个迭代器对象。
2.1、创建生成器函数
创建生成器函数的基础语法如下:
function* 函数名() {
}
在创建生成器函数时,需要在 function
关键字和函数名之间,添加一个 *
星号。
2.2、调用生成器函数
要调用生成器函数,和普通函数一样,还是要先通过 函数名()
的形式来实现。但是,当我们调用完后会发现,函数内的代码并没有执行。
因为通过 函数名()
的形式调用生成器函数,会得到一个迭代器对象,如果需要执行生成器函数内部的代码,需要调用迭代器的 next()
方法。
生成器函数的调用基础语法如下:
const iterator = 函数名();
iterator.next();
2.3、yield 关键字
在生成器函数中,可以使用 yield
关键字。
yield
关键字可以用来阻塞生成器函数中代码的执行,也就是说,当生成器函数中的代码执行到 yield
时,会暂停执行。
例如:
function* myGenerator() {
console.log('1、hello');
console.log('2、hello');
yield;
console.log('3、hello');
}
const iterator = myGenerator();
iterator.next();
如果希望生成器函数中代码继续往后执行,需要再次调用 next()
。
function* myGenerator() {
console.log('1、hello');
console.log('2、hello');
yield;
console.log('3、hello');
}
const iterator = myGenerator();
iterator.next(); // { value: undefiend, done: false }
iterator.next(); // { value: undefiend, done: true }
每一次调用 next()
方法,该方法都会返回一个对象,包含 value
和 done
属性,done
属性可以用来表示当前函数是否执行完成,如果 done: true
,则表示函数内的代码已经执行完毕。
1)yield 搭配表达式
在 yield
关键字的后面,还可以跟一个表达式。基础语法如下:
function* 函数名() {
// 函数体内容
yield 表达式;
}
yield
关键字后面的值,会作为 next()
返回对象的 value
属性值。
例如:
function* myGenerator() {
yield 'hello';
}
const iterator = myGenerator();
iterator.next(); // { value: 'hello', done: false }
也就是说,我们可以通过 yield 表达式
,将生成器函数内部的数据传递到函数外部。
2)yield 的返回值
当我们调用 next()
方法时,可以通过 next()
方法往生成器函数中传递数据,最终,这个数据会作为上一次 yield
的返回值接收。
案例代码:
function* myGenerator() {
console.log(1);
const num = yield;
console.log(num); // 2
}
const iterator = myGenerator();
iterator.next(); // 1
iterator.next(2);
2.4 使用Generator函数进行异步编程
function getPromise(params,timeout){
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve(`response:${params}`)
}, timeout);
})
}
function * gen(){
try {
yield getPromise('success1',2000)
yield getPromise('success2',1000)
yield getPromise('success3',3000)
yield getPromise('success4',4000)
yield getPromise('success1',2000)
} catch (error) {
console.log(error);
}
}
for(let result of gen()){
console.log(result);
result.then(value=>{
console.log(`执行的结果是:${value}`);
})
}
二、Redux-saga
1.Redux-saga概述
redux-saga是一个用于管理应用程序Side Effect副作用(例如:异步获取数据,访问浏览器缓存等)的library,它的目的是让副作用管理更加的简单,执行更加高效。
redux-saga就是redux的一个中间件,可以通过正常的redux action从主应用程序启动,暂停和取消,它可以访问完整的redux saga,也能够dispatch redux action
redux-saga使用了ES6的Generator功能,让异步流程更加易于读取,写入和测试,通过这种方式,让异步看起来更加像标准同步的js代码。
Redux官方网址:Redux-Saga - An intuitive Redux side effect manager. | Redux-Saga
2、redux-saga的安装
yarn add redux-saga
3、redux-saga的原理图
3、redux-saga获取购物车信息
1)在src/components/ShopCartList.jsx中编写如下代码
import React,{useEffect} from 'react'
import {useDispatch} from 'react-redux'
export default function ShopCartList() {
const dispatch=useDispatch()
useEffect(()=>{
dispatch({type:'getShopCartListAsync'})
},[])
return (
<div>
<h2>购物车列表</h2>
</div>
)
}
如上代码,主要是在页面加载完毕后,向saga文件dispatch一个请求
-
在redux/saga.js文件中,编写如下方法,主要完成监听组件发送过来的通知
import {takeEvery} from 'redux-saga/effects'
function * getShopCartList(){
console.log('--------准备向网络购物车信息-----------');
}
function * index(){
yield takeEvery('getShopCartListAsync',getShopCartList)
}
export default index
-
在src/redux/store.js中,编写如下
import {legacy_createStore as createStore,applyMiddleware} from 'redux'
import shopCartListReducer from './reducer/shopCartListReducer'
import createSagaMiddleware from 'redux-saga'
import saga from '../redux/saga'
const sagaMiddleware=createSagaMiddleware()
const store=createStore(shopCartListReducer,applyMiddleware(sagaMiddleware))
sagaMiddleware.run(saga)
export default store
4)在index.js文件上使用<Provider>
将跟组件进行包裹
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import {Provider} from 'react-redux'
import store from './redux/store'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
5)在redux/saga.js文件中,完成异步网络请求,并将结果通过reducer保存到仓库中
function * getShopCartList(){
const result=yield call(api.shopcart.getshopcartListApi)
console.log(result.data.data);
yield put(shopcartListAction(result.data.data))
}
6)在src/components/ShopCartList.jsx组件中通过useSelector钩子函数来接收仓库中的数据
const list=useSelector((state)=>{
return state.shopcartList
})
7)然后在购物车组件中将列表数据渲染出来
import React,{useEffect,useMemo} from 'react'
import {useDispatch,useSelector} from 'react-redux'
export default function ShopCartList() {
const dispatch=useDispatch()
useEffect(()=>{
dispatch({type:'getShopCartListAsync'})
},[])
//从仓库中获取购物车列表数据
const list=useSelector((state)=>{
return state.shopcartList
})
//完成计算商品总价
const total=useMemo(()=>{
return list.filter(item=>item.checked).reduce((pre,cur)=>pre+cur.price*cur.num,0)
},[list])
return (
<div>
<h2>购物车列表</h2>
<table>
<thead>
<tr>
<td>序号</td>
<td>名称</td>
<td>单价</td>
<td>数量</td>
<td>小计</td>
<td>更新时间</td>
</tr>
</thead>
<tbody>
{
list.map((item,index)=><tr key={item._id}>
<td>
<input type="checkbox" checked={item.checked}/>
</td>
<td>
{item.name}
</td>
<td>
{item.price}
</td>
<td>
<button>-</button>
{item.num}
<button>+</button>
</td>
<td>
{item.price*item.num}
</td>
<td>
{item.updateDate}
</td>
</tr>)
}
<tr>
<td colSpan={5}>
总价:{total}
</td>
</tr>
</tbody>
</table>
</div>
)
}
4、redux-saga更新购物车商品数量
1)在ShopCart.jsx组件中绑定事件
<button onClick={()=>{changeNum(item._id,-1)}}>-</button>
{item.num}
<button onClick={()=>{changeNum(item._id,1)}}>+</button>
const changeNum=(_id,n)=>{
dispatch({type:'changeNumAsync',_id,n})
}
-
在saga.js文件中添加监听方法
function * index(){
yield takeEvery('changeNumAsync',changeNum)
}
-
在saga.js文件中编写changNum生成器方法,用来实现异步请求
function * changeNum({_id,n}){
const result=yield call(api.shopcart.changeNumApi,{_id,n})
if(result.data.code){
yield put({type:'getShopCartListAsync'})
}
}
5、redux-saga实现勾选商品信息
1)在ShopCart.jsx组件中绑定事件
<input type="checkbox" checked={item.checked} onChange={()=>{checkedItem(item._id)}}/>
const checkedItem=(_id)=>{
dispatch({type:'checkedItemAsync',_id})
}
2)在saga.js文件中添加监听方法
function * index(){
yield takeEvery('checkedItemAsync',checkedItem)
}
3)在saga.js文件中编写checkedItem生成器方法,用来实现异步请求
function * checkedItem({_id}){
const result=yield call(api.shopcart.checkProductApi,_id)
if(result.data.code){
yield put({type:'getShopCartListAsync'})
}
}