redux-saga

16 篇文章 0 订阅
8 篇文章 0 订阅

redux-sage

sagaAPI: https://redux-saga-in-chinese.js.org/docs/api/

  • 纯净
  • 强大
  • 灵活

在saga任务中,如果yield了一个普通数据,saga不作任何处理,仅仅将数据传递给yield表达式(把得到的数据放到next的参数中),因此,在saga中,yield一个普通数据没什么意义。

saga需要你在yield后面放上一些合适的saga指令(saga effects), 如果放的是指令,saga中间件会根据不同的指令进行特殊处理。以控制整个任务的流程

每个指令本质上就是一个函数,该函数调用后,会返回一个指令对象,saga会接收到对该指令对象,进行各种处理

一旦saga任务完成(生成器函数),则saga中间件一定结束

  • take指令: 【阻塞】监听某个action,如果action发生了,则会进行下一步处理,take指令仅监听一次。yield得到一个完美的对象。

  • all指令: 【阻塞】该函数传入一个数组,数组中放入生成器,saga会等待所有的生成器全部完成后才会进一步处理

  • takeEvery指令: 不断的监听某个action, 当某个action到达之后,运行一个函数。takeEvery永远不会结束当前的生成器

  • delay指令:【阻塞】 阻塞指定的毫秒数

  • put指令: 用于重新触发action, 相当于dispatch一个action

  • call指令: 【可能阻塞】 用于副作用的(通常用于异步)函数调用

  • apply指令: 与call类似

  • select指令: 用于得到当前仓库的数据

  • cps指令: 【可能阻塞】用于调用那些传统的回调方法的异步函数

  • fock指令: 用于开启几个新的任务,该任务不会阻塞,该函数需要传递一个生成器函数,fork返回一个对象,类型为task, 创建一个 Effect 描述信息,用来命令 middleware 以 非阻塞调用 的形式执行 fn。

  • cancel指令:用于取消一个或多个任务,实际上,取消的实现原理,是利用generator。return 。 cancel可以不传递参数,取消自己 创建一个 Effect 描述信息,用来命令 middleware 取消之前的多个分叉任务。

  • takeLast俄速通: 功能和takeEvery一致,只不过,会自动取消掉之前开启的任务

  • cancelled指令:判断担起任务线是否被取消掉了

  • race指令:【竞赛】 可以传递多个指令,当其中一个指令结束后,会直接结束,与promise。rece类似。

手写saga

saga中间件的原理:

  1. 首先启动一个任务
  2. 当action触发时,直接将action分发到下一个中间件

源码分析:

  1. runSaga:一个函数,用于启动一个任务,一个任务的本质是一个generator
    function,runSaga在内部得到该函数的generator,并控制生成器的每一次迭代。

redux-saga

index


import runSaga from './runSaga'
/**
 * saga 中间件的函数
 */


 export default function() {
     return function sagaMiddleWare(store) {
        const env = {
            store
        }

        sagaMiddleWare.run = runSaga.bind(null, env)
         return function (next) {
             return function(action){
                return next(action); //直接交给下一个中间件
             }
         }
     }
 }


runSaga.js

import { Task } from "./Task";
import isGenerator from 'is-generator'
// import  from 'is-promise'
import { isEffect } from './effectHelper'
import isPromise from "is-promise";
import runEffect from './runEffect'

/**
 * 开启一个新函数
 * @param {*} env 全局环境的数据,被saga执行期间
 * @param {*} generatorFunc  生成器函数
 * @param {*} args 生成器函数的参数
 */
export default function(env, generatorFunc, ...args) {
    console.log("一个新的任务启动了");

    const iterator = generatorFunc(...args)
    if (isGenerator(iterator)){
        //不断调用next, 直到迭代结束
        next();
    }else{
        console.log("一个普通函数");
    }

    /**
     * 
     * @param {*} nextvalue 正常调用iterator.next时传递的值
     * @param {*} err 错误对象
     * @param {*} isOver 是否结束
     */

    function next(nextvalue, err, isOver){
        let result; // 记录迭代的结果{value: xxx , done: false }
        if(err) {
        //情况2: 调用iterator.throw(err)
            iterator.throw(err)
        }else if(isOver) {
        //情况3: 调用iterator.return 
            result = iterator.return();//结束迭代
        }else{
        //情况1: 调用iterator.next(value)
            result = iterator.next(nextvalue)
        }
        
        const { value, done } = result 
        if(done) {
            //迭代器结束
            return;
        }
        //没有结束
        if(isEffect(value)){
            console.log("是一个effect");
            runEffect(env, value, next)
        }else if(isPromise(value)) {
           //不是一个 effect
           // 情况1:value是promisi
            console.log("不是一个effect,是promise");
            value.then(r=>next(r))
                .catch(err => next(null, err))
        }else{
            //情况2:其他
            console.log("不是一个effect是其他");
            next(value); //直接下一步
        }
    }
    return new Task()
}

effectHelper.js

//该模块为创建effect和判断effect提供支持

/**
 * effect 的可用类型
 */

 export const effectTypes = {
     CALl: "CALL",
     TAKE: "TAKE",
     FORK: "FORK",
     ALL: "ALL",
     PUT: "PUT"
     
 }

 /**
  * 
  * @param {*} type 有效的effect类型
  * @param {*} payload 
  */

export function createEffect(type, payload){
    // 验证 type
    if (!Object.values(effectTypes).includes(type)){
        throw new TypeError("这是无效的type值")
    }

    return {
        "@@redux-saga/IO": true,
        type,
        payload 
    }

}

/**
 * 判断一个对象是否是effect
 * @param {*} obj 
 */
export function isEffect(obj) {
    if(typeof obj !== "object") {
        return false;
    }
    if(obj["@@redux-saga/IO"] === true){
        return true
    }
    return false
}








runEffect.js


import { effectTypes } from 'redux-saga/effects';
import effctHelper from './effectHelper';
import {runCallEffect} from './effects/call'

//该模块主要用于处理一个effect对象

/**
 * 处理一个effect对象 根据不同的effect。type值进行不同的处理
 * @param {*} env 全局的环境对象
 * @param {*} value effect对象
 * @param {*} next 下一个处理
 */
export default function (env, effect, next) {
    switch (effctHelper.type){
        case effectTypes.CALL:
            runCallEffect(env, effect, next)
            break;
        default:
            throw new Error("类型无效");
    }
}

redux-saga\effects

index.js

export { call } from './call'
export { delay } from './delay'
export { pull } from './pull'

call.js

import { createEffect, effectTypes } from '../effectHelper';
import isPromise from 'is-promise'

//1.  提供一个call函数用于产生call effect
export function call(fn, ...args){
    var context = null , //this的指向
        func = fn; //要运行的函数
    if(Array.isArray(fn)){
        context = fn[0]; //this的指向数组的第一项
        func = fn[1]; //运行函数指向数组的第二项
    }
    return createEffect(effectTypes.CALl, {
        context,
        fn: func,
        args
    })
}

//2. 处理call effect
export function runCallEffect(env, effect, next) {
    const { context, fn, args} = effect.payload
    const result = fn.call(context,...args)
    if(isPromise(result)){
        result.then(v=>next(v))
            .catch(err => next(null, err))
    }
    else{
        next(result)
    }
}


delay.js

import { call } from './call'

export function delay(duration) {
    return call(function(){
        return new Promise(resolve => {
            setTimeout(() => {
                resolve();
            }, duration);
        })
    })
}


pull.js

import { createEffect, effectTypes } from '../effectHelper'

export function put(action) {
    return createEffect(effectTypes.PUT, { 
        action
    })

}

export function runPutEffect(env, effect, next) {
    const action = effect.payload.action;
    const result = env.store.dispatch(action)
    next(result)
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值