美文网首页
Redux-Saga的理解与实现

Redux-Saga的理解与实现

作者: 吴晗君 | 来源:发表于2019-07-21 18:54 被阅读0次

    基础流程

    在saga中需要对不同的业务写不同的generator函数,然后通过take(redux-saga暴露的方法)监听对应的action-type,内部是通过发布订阅存储在内存中。订阅的回调函数就是内部的一个next方法,这个next的作用是:通过generator生成的iterator中的next方法来持续迭代generator函数。

    当组件中dispatch对应的action type的时候,redux-saga中间件就会publish(action type),来执行订阅函数。这个时候,就会执行对应generator的next方法。比如这时候就去请求数据,也是通过yield API.getSomething()的写法来拿到数据,然后在下一步通过put来发出修改redux中store数据的action。执行put的时候,会在next函数中直接dispatch出一个真实的action,来改变仓库中的数据。

    知识点

    主要点在于是在yield的用法,以及如何通过发布订阅与React组件、yield函数执行发生联系。以及对多种情况的公共处理:take、takeEvery、put、delay、fork、call、cps、cancel。

    用法

    import { put, all, call, take, takeEvery, fork, cancel } from "../../redux-saga/effects"
    import * as types from '../action-types'
    
    function delay (ms) {
      return new Promise((resolve) => setTimeout(resolve, ms, ms))
    }
    
    // function* incrementAsync () {
    //   yield call(delay, 1000)
    //   yield put({type: types.INCREMENT})
    // }
    
    function* watchIncrementAsync() {
      debugger
      // for (let i=0;i<3;++i) {
      //   // yield take(types.ASYNC_INCREMENT)
      //   // yield call(delay, 1000)
      //   yield put({type: types.INCREMENT})
      // }
      yield put({type: types.INCREMENT})
      console.log('所有generator函数已完成执行')
    }
    
    function* helloSaga (actionType) {
      console.log('logger', actionType)
      // const task = 
      // yield fork(task)
    }
    
    // 自执行每秒加1
    function* increment () {
      while(true){
        console.log('22222')
          yield delay(1000) 
          console.log('11111')
          yield put({type:types.INCREMENT})
      }
    }
    
    function* incrementWatcher () {
      const task = yield fork(increment)
      console.log('incrementWatcher')
      yield take(types.CANCEL_INCREMENT)
      console.log('take')
      yield cancel(task)
    }
    
    export default function* rootSaga () {
      console.log(666666)
      // debugger
      // yield watchIncrementAsync()
      yield incrementWatcher()
      // all([
      //   incrementWatcher()
      //   // takeEvery(types.ASYNC_INCREMENT, helloSaga)
      // ])
      // yield takeEvery(types.ASYNC_INCREMENT, helloSaga)
      // yield takeEvery(types.ASYNC_INCREMENT, watchIncrementAsync)
      console.log('所有generator函数全都执行完毕')
    }
    

    实现

    effect.js

    export function call (fn, ...args) {
      return {
        type: 'CALL',
        fn,
        args
    }
    }
    
    export function put (action) {
      return {
        type: 'PUT',
        action
      }
    }
    
    export function take (actionType) {
      return {
        type: 'TAKE',
        actionType
      }
    }
    
    // fork的作用是不阻塞generetor函数,再开一个generator函数来做对应的事情。
    export function fork (task) {
      return {
        type: 'FORK',
        task
      }
    }
    
    export function* takeEvery (actionType, task) {
      yield fork(function* () {
        while(true) {
          yield take(actionType)
          yield task(actionType)
        }
      })
    }
    
    const innerDelay= ms => new Promise((resolve) => setTimeout(resolve, ms, ms))
    export function delay(...args) {
      return call(innerDelay, ...args)
    }
    // 用以处理回调函数的形式
    export function cps (fn, ...args) {
      return {
          type: 'CPS',
          fn,
          args
      }
    }
    
    export function all (fns) {
      return {
          type: 'ALL',
          fns
      }
    }
    
    export function cancel (task) {
      return {
          type: 'CANCEL',
          task
      }
    }
    

    createSagaMiddleware.js

    const isIterator = (it) => typeof it[Symbol.iterator] === 'function'
    const isPromise = (o) => typeof o.then === 'function'
    const times = (cb, total) => {
      for (let i = 0; i < total; ++i) {
        if(i === total){
          cb()
        }
      }
    }
    
    export default function createSagaMiddleware () {
      function createChannel () {
        let _listeners = {}
        function subscribe (actionType, cb) {
          console.log(`订阅${actionType}`)
          _listeners[actionType] = _listeners[actionType] || []
          const once = () => {
            cb()
            off(actionType, once)
          }
          once._o = cb
          _listeners[actionType].push(once)
        }
    
        function publish(actionType) {
         const nexts =  _listeners[actionType]
         if (nexts && nexts.length) {
          nexts.forEach(next => next())
         }
        }
    
        function off (key, cb) {
          if (_listeners[key]) {
            _listeners[key] = _listeners[key].filter(l => l !== cb && l._o !== cb)
          }
        }
        return {
          subscribe,
          publish
        }
      }
      // 创建监听管道,其实就是发布订阅
      let channel = createChannel()
      const sagaMiddleware = ({dispatch, getState}) => {
        function run (gen, cb) {
          const it = isIterator(gen) ? gen : gen()
          function next (returnValue) {
            // effect是saga出得一个概念,指的是
            // 一些简单 Javascript 对象,包含了要被 middleware 执行的指令。 
            // 当 middleware 拿到一个被 Saga yield 的 Effect,它会暂停 Saga,
            // 直到 Effect 执行完成,然后 Saga 会再次被恢复。
    
            // next函数参数是上一次yield 返回值。
            const {value:effect, done} = it.next(returnValue)
            if (!done) {
              if (isIterator(effect)) {
                run(effect)
                next()
              } else if (isPromise(effect)) { // 处理yield + promise的情况
                effect.then(next)
              } else {
                const {type, actionType, action, task, fn, args, fns} =effect
                switch (type) {
                  case 'TAKE': 
                  // take会再yield的地方等待,
                  // 等组件dispatch的时候,调用next执行监听的内容(写在take()后面的代码)。
                    console.log('take----', actionType)
                    channel.subscribe(actionType, next)
                    break;
                  case 'PUT':
                    console.log('put----')
                    dispatch(action)
                    next() // put不阻塞
                    break;
                  case 'FORK':
                    console.log('fork----')
                    const newTask = task()
                    run(newTask)
                    next(newTask)
                    break;
                  case 'CALL': // call返回的都是promise
                      console.log('call----')
                      fn(...args).then(next)
                      break;
                  case 'CPS':
                    console.log('cps----')
                    fn(...args, next)
                    break;
                  case 'ALL': // 直到所有all完成(所有generator函数调用next函数皆返回{d}one:true})
                    console.log('all----')
                    const done = times(next, fns.length)
                    fns.forEach((fn) => run(fn, done))
                    break;
                  case 'CANCEL': // 直到所有all完成(所有generator函数调用next函数皆返回{d}one:true})
                    console.log('cancel----', task)
                    task.return('中断执行')
                    break;
                }
              }
            } else {
              cb && cb()
            }
          }
          next()
        }
        // run放到sagaMiddleware函数里面的目的是需要拿到dispatch方法
        sagaMiddleware.run = run
        return (next) => (action) => {
          const type = action.type
          channel.publish(type)
          next(action)
        }
      }
      
      return sagaMiddleware
    }
    

    相关文章

      网友评论

          本文标题:Redux-Saga的理解与实现

          本文链接:https://www.haomeiwen.com/subject/dayulctx.html