redux-saga

一、迭代器和生成器

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() 方法,该方法都会返回一个对象,包含 valuedone 属性,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一个请求

  1. 在redux/saga.js文件中,编写如下方法,主要完成监听组件发送过来的通知

import {takeEvery} from 'redux-saga/effects'
function * getShopCartList(){
    console.log('--------准备向网络购物车信息-----------');
}

function * index(){
    yield takeEvery('getShopCartListAsync',getShopCartList)
}

export default index
  1. 在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})
}
  1. 在saga.js文件中添加监听方法

function * index(){
    yield takeEvery('changeNumAsync',changeNum)
}
  1. 在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'})
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值