redux-saga学习笔记

在学习redux-saga以前,咱们必须了解ES6 Generators [ES6 Generators]html

介绍 

中间件:将具体业务和底层逻辑解耦的组件。redux中,中间件就是一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其余功能。 redux

附上官网redux-中间件介绍
api

redux应用中最经常使用的两种异步流处理方式: redux-thunk,redux-sagapromise

redux-saga 是一个用于管理 Redux 应用异步操做(Side Effects。译注:直译成 “反作用” 不太通顺,因此这里译为 “异步操做” 更好理解)的中间件(又称异步 action)。 redux-saga 经过建立 Sagas 将全部的异步操做逻辑收集在一个地方集中处理,能够用来代替 redux-thunk 中间件。
bash

它具备以下特性:
babel

  • 集中处理 redux 反作用问题。架构

  • 被实现为 generator 。并发

  • 类 redux-thunk 中间件。app

  • watch/worker(监听->执行) 的工做形式。异步

如何使用 

  • 建立与运行
  • 测试(遍历 generator 并对它的值(gen.next() // => { done: ..., value: ... } )作 deepEqual 测试)

redux-saga [官方文档]有详细的使用说明 

redux-saga [中文文档]

概念 



使用 Saga 辅助函数 

(经常使用)

  • Middleware API
    • createSagaMiddleware(...sagas)
    • middleware.run(saga, ...args)
  • Saga Helpers

    • takeEvery(pattern, saga, ...args) :接受Action,而且触发某个方法(一個 Generator function) 实例同时启动
    • takeLatest(pattern, saga, ..args) :屡次触发,去最后一次
  • Effect creators

    • take(pattern) :建立一条 Effect 描述信息,指示 middleware 等待 Store 上指定的 action。 Generator 会暂停,直到一个与 pattern 匹配的 action 被发起。
    • put(action) : put(action) 用于触发 action,功能上相似于dispatch。
    • fork(fn, ...args) :建立一条 Effect 描述信息,指示 middleware 以 无阻塞调用 方式执行 fn。fork 相似于 call,能够用来调用普通函数和 Generator 函数。但 fork 的调用是无阻塞的,在等待 fn 返回结果时,middleware 不会暂停 Generator。 相反,一旦 fn 被调用,Generator 当即恢复执行。 fork 与 race 相似,是一个中心化的 Effect,管理 Sagas 间的并发。
    • call(fn, ...args) :cal阻塞型调用,有阻塞地调用 saga 或者返回 promise 的函数。
    • join(task): 建立一个效果描述,指示中间件等待先前分叉的任务的结果。
    • cancel(task): 是一个无阻塞 Effect。也就是说,Generator 将在取消异常被抛出后当即恢复。
    • select(selector, ...args) : 做用和 redux thunk 中的 getState 相同。

      const id = yield select(state => state.id);复制代码

  • Effect combinators

    • race(effects) :rece Effect提供了一个方法,在多个Effects之间触发一个竞赛。在race Effect中,全部参与竞赛的任务,除了优胜者,其余任务都会被取消
  • Interfaces

    • Task :Task 接口指定了经过 forkmiddleware.runrunSaga 执行 Saga 的结果。

redux-saga API详解能够查看 [API 参考] 

声明式 Effects

Effect 是一个简单的对象,该对象包含了一些给 middleware 解释执行的信息。能够经过使用 effects API 如 fork,call,take,put,cancel 等来建立 Effect。( redux-saga [API 参考]

触发一个action 到 reducer的过程当中,如 yield call(delay, 1000)即 yield 了下面的对象,call 建立了一条描述结果的信息,而后,redux-saga middleware 将确保执行这些指令并将指令的结果返回给 Generator 

从 Saga 内触发异步操做(Side Effect)老是由 yield 一些声明式的 Effect 来完成的 (你也能够直接 yield Promise,可是这会让测试变得困难。使用 Effect 诸如 call 和 put,与高阶 API 如 takeEvery 相结合,又有额外的易于测试的好处。

发起action

 redux-saga 提供了另一个函数 put,这个函数用于建立 dispatch Effect

  •  yield put({ type: '',... }) 

yield关键字用来暂停和恢复一个生成器函数,yield关键字使生成器函数执行暂停,yield关键字后面的表达式的值返回给生成器的调用者。它能够被认为是一个基于生成器的版本的return关键字。

yield关键字实际返回一个IteratorResult对象,它有两个属性,value和done。value属性是对yield表达式求值的结果,而done是false,表示生成器函数还没有彻底完成。

错误处理 

try/catch 语法在 Saga 中捕获错误 

redux-saga 是如何工做的? 

Redux Saga能够理解为一个和系统交互的常驻进程,其中,Saga可简单定义以下: 

  • Saga = Worker + Watcher

Sagas经过Generator函数来建立,采用 Generator 函数来 yield Effects,只会在应用启动时调用,咱们能够把它看做是在后台运行的进程,Sagas监听发起的action,而后决定基于这个action来作什么,是发起异步调用仍是发起其余的action到Store,甚至是调用其余的Sagas

  •  sagas 包含3个部分,用于联合执行任务: 

Watcher/Worker 指的是一种使用两个单独的 Saga 来组织控制流的方式。

 1. root saga:当即启动 sagas 的惟一入口 

 2. Watcher: 监听发起的 action 并在每次接收到 action 时 fork 一个 worker。

 3. Worker: 处理 action 并结束它。

 eg: 

//动态执行 rootSaga。用于 applyMiddleware 阶段以后执行 rootSaga。
middleware.run(rootSaga, ...args)
...

//Watcher/Worker

function* watcher() {
  while(true) {
    const action = yield take(ACTION)
    yield fork(worker, action.payload)
  }
}

function* worker(payload) {
  // ... do some stuff
}
复制代码

VS redux-thunk 

  • 使用redux-thunk

    try {
            dispatch({ type: LOGIN_REQUEST });
            let { data } = await request.post('/login', { user, password });
            await dispatch(loadUserData(data.uid));
            dispatch({ type: LOGIN_SUCCESS, data });
        } catch(error) {
            dispatch({ type: LOGIN_ERROR, error });
        }
    }
    export const loadUserData = (uid) => async (dispatch) => {
        try {
            dispatch({ type: USERDATA_REQUEST });
            let { data } = await request.get(`/users/${uid}`);
            dispatch({ type: USERDATA_SUCCESS, data });
        } catch(error) {
            dispatch({ type: USERDATA_ERROR, error });
        }
    }
    复制代码

  • 使用redux-saga

    export function* loginSaga() {
      while(true) {
        const { user, pass } = yield take(LOGIN_REQUEST) //等待 Store 上指定的 action LOGIN_REQUEST
        try {
          let { data } = yield call(request.post, '/login', { user, pass }); //阻塞,请求后台数据
          yield fork(loadUserData, data.uid); //非阻塞执行loadUserData
          yield put({ type: LOGIN_SUCCESS, data }); //发起一个action,相似于dispatch
        } catch(error) {
          yield put({ type: LOGIN_ERROR, error });
        }  
      }
    }
    
    export function* loadUserData(uid) {
      try {
        yield put({ type: USERDATA_REQUEST });
        let { data } = yield call(request.get, `/users/${uid}`);
        yield put({ type: USERDATA_SUCCESS, data });
      } catch(error) {
        yield put({ type: USERDATA_ERROR, error });
      }
    }
    复制代码

  • 相比Redux Thunk,使用Redux Saga有几处明显的变化: 

1. 在组件中,再也不dispatch(action creator),而是dispatch(pure action) 

2. 组件中再也不关注由谁来处理当前action,action经由root saga分发 

3. 具体业务处理方法中,经过提供的call/put等帮助方法,声明式的进行方法调用 

4. 使用ES6 Generator语法,简化异步代码语法 5. redux-saga 将异步任务进行了集中处理,且方便测试。

结论 

redux-saga的使用更多的是根据我的需求与习惯

redux-saga 的优势: 

1. 声明式 Effects:全部的操做以JavaScript对象的方式被 yield,并被 middleware 执行。使得在 saga 内部测试变得更加容易,能够经过简单地遍历 Generator 并在 yield 后的成功值上面作一个 deepEqual 测试。

 2. 高级的异步控制流以及并发管理:可使用简单的同步方式描述异步流,并经过 fork 实现并发任务。 

3. 架构上的优点:将全部的异步流程控制都移入到了 sagas,UI 组件不用执行业务逻辑,只需 dispatch action 就行,加强组件复用性。 

目前在项目实践中遇到的一些问题:

 1. redux-saga 不强迫咱们捕获异常,这每每会形成异常发生时难以发现缘由。所以,一个良好的习惯是,相信任何一个过程都有可能发生异常。 

2. generator 的调试环境比较糟糕,babel 的 source-map 常常错位,常常要手动加 debugger 来调试。 

4. 在action的定义上要谨慎,避免action在saga和reducer之间重复触发,形成死循环 

相关文章
相关标签/搜索