Redux-saga-整理

image


介紹

  1. 在redux中更好的解決異步操做
  2. redux-saga至關於在redux原來的數據流中多了一層,對action進行監聽
  3. 接收到action時,派發一個任務維護state
  4. saga經過Generator方式建立,異步方法同步化

正常redux流程 javascript

加入redux-saga以後的流程 java


使用方式

import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
//引入saga文件
import { rootSaga } from './rootSaga'
//使用 redux-saga 模塊的 createSagaMiddleware 工廠函數來建立一個 Saga middleware。
//運行 rootSaga 以前,咱們必須使用 applyMiddleware 將 middleware 鏈接至 Store。而後使用 

const sagaMiddleware = createSagaMiddleware();
const middlewares = [ sagaMiddleware ];

const store = createStore(rootReducer, applyMiddleware(...middlewares));
sagaMiddleware.run(rootSaga);
複製代碼

redux-saga 輔助函數

sage提供了一些輔助函數,包裝了一些內部方法,用來在一些特定的action被髮起到store時派生任務ios

takeEvery

import { call, put } from 'redux-saga/effects'
export function* fetchData(action) {
   try {
      const data = yield call(Api.fetchUser, action.payload.url);
      yield put({type: "FETCH_SUCCEEDED", data});
   } catch (error) {
      yield put({type: "FETCH_FAILED", error});
   }
}

function* watchFetchData() {
  yield* takeEvery('FETCH_REQUESTED', fetchData)
}

//有一種用法:監控全部的發起的action
yield takeEvery('*', fn)
複製代碼

takeEvery 容許多個 fetchData 實例同時啓動, 在某個特定時刻, 儘管以前還有一個或多個fetchData還沒有結束, 咱們仍是能夠啓動一個新的fetchData任務-->意思就是隻用調用了 FETCH_REQUESTED action的時候就會啓動 fetchData 任務.redux

takeLatest

function* watchFetchData() {
  yield* takeLatest('FETCH_REQUESTED', fetchData)
}
複製代碼

在任什麼時候刻 takeLatest只容許一個 fetchData 任務在執行,而且這個任務是最後被啓動的那個,若是以前有一個任務再啓動的時候執行了fetchData , 那麼以前的任務會被自動取消 -- 能夠得到最後一次(最新)調用FETCH_REQUESTED action 獲得的結果.axios


Effects

概念

sagas都是Generator函數實現,能夠用yield 對 js 對象來表達saga的邏輯,這些對象就是effect,api

  1. sagas都是用Generator函數實現的
  2. 在 Generator 函數中,yield 右邊的任何表達式都會被求值,結果會被 yield 給調用者
  3. 用yield對Effect(簡單對象),進行解釋執行
  4. Effect 是一個簡單的對象,這個對象包含了一些給 middleware 解釋執行的信息。 你能夠把 Effect 看做是發送給 middleware 的指令以執行某些操做(調用某些異步函數,發起一個 action 到 store,等等)
//官方例子
import { takeEvery } from 'redux-saga/effects'
import Api from './path/to/api'

//監聽若是有一個調用PRODUCTS_REQUESTED 的action的話,就會匹配到第二個參數所表明的effect
function* watchFetchProducts() {
  yield takeEvery('PRODUCTS_REQUESTED', fetchProducts)
}
//執行,獲取數據
//使用Generator 調用了Api.fetch,在Generator函數中,yield右面的任何表達式都會被求值,結果會被yield給調用者
function* fetchProducts() {
  const products = yield Api.fetch('/products')
  console.log(products)
}

//第二種方式
import { call } from 'redux-saga/effects'
//call(fn, ...args) 這個函數。與前面的例子不一樣的是,如今咱們不當即執行異步調用,相反,call
//建立了一條描述結果的信息就像在 Redux 裏你使用 action 建立器,建立一個將被 Store 執行的、描述 action 的純文本對象。
//call 建立一個純文本對象描述函數調用。redux-saga middleware 確保執行函數調用並在響應被 resolve 時恢復 generator
function* fetchProducts() {
  const products = yield call(Api.fetch, '/products')
  // ...
}

複製代碼

發送action到store

//這種方式是Generator獲取到了返回值,在調用dispatch
function* fetchProducts(dispatch) const products = yield call(Api.fetch, '/products') dispatch({ type: 'PRODUCTS_RECEIVED', products }) } import { call, put } from 'redux-saga/effects'
//...
function* fetchProducts() {
  const products = yield call(Api.fetch, '/products')
  // 建立並 yield 一個 dispatch Effect
  yield put({ type: 'PRODUCTS_RECEIVED', products })
}
複製代碼

錯誤處理

import Api from './path/to/api'
import { call, put } from 'redux-saga/effects'

function* fetchProducts() {
  try {
    const products = yield call(Api.fetch, '/products')
    yield put({ type: 'PRODUCTS_RECEIVED', products })
  }
  catch(error) {
    yield put({ type: 'PRODUCTS_REQUEST_FAILED', error })
  }
}

複製代碼

使用 try/catch 的方式捕獲saga的錯誤信息數組


一些概念

從 Saga 內觸發異步操做(Side Effect)老是由 yield 一些聲明式的 Effect 來完成的 , 一個 Saga 所作的其實是組合那些全部的 Effect,共同實現所需的控制流。使用上是用yield Effects的方式來完成,Effect包括併發

  1. call: yield call(Generator, param) yield一個call,命令middleware以參數param調用函數 到Generator,saga等待Generator執行以後,接收返回值繼續執行,call是堵塞的,就是等待執行完再繼續執行,返回的是執行完正常返回的結果.
  2. take: 是阻塞的,只有監聽到他(action.type),纔會繼續往下執行.也就是說建立一個effect的描述信息,用來命令middleware在Store上等待指定action,在發起與他相匹配的action以前,Generator將暫停.
  3. put: 相似dispatch方法,觸發一個action,用來命令middleware向Store發起一個action請求,並且是非阻塞的
  4. fork: 非阻塞的,遇到它不須要等待他執行完畢,就能夠繼續往下執行,fork返回的是一個任務,能夠被取消
  5. cancel: 針對fork方法返回的任務,進行取消
  6. select: 能夠從全局state中獲取狀態
  7. saga: 就是用* 註冊的函數,一個函數就是一個saga
  8. effect: 上面的call,put,take...就是effect
function* watchAndLog() {
  while (true) {
    const action = yield take('*')
    const state = yield select()
  }
}
take,它將會暫停 Generator 直到一個匹配的 action 被髮起了,watchAndLog 處於暫停狀態,直到任意的一個 action 被髮起。

複製代碼

無阻塞調用-fork

fork一個任務,任務會在後臺啓動,調用者也能夠繼續它的流程,而不用等待被fork的任務執行結束 當咱們須要有併發操做的時候,使用call effect會阻塞saga的執行,使用fork就須要關心被阻塞,或者等待結果返回在繼續執行app

const result = yield fork (saga,param)
複製代碼

同時執行多個任務

const [users, repos] = yield [
  call(fetch, '/users'),
  call(fetch, '/repos')
]
複製代碼

當須要同步執行多個任務,須要把yield一個包含了effect的數組,Generator將會阻塞,等全部的effect都執行完畢異步


使用

//redux.connect所須要綁定到props上的action
function mapDispatchToProps(dispatch) {
    return {
        getHome: bindActionCreators(getHomeAdData, dispatch)
    }
}
//一個 action creator
export function getHomeAdData(){
    return {
        type: actionTypes.HOME_AD_DATA,
    }
}
//監聽action.type,而後出發後面的action
export default function* rootSaga () {
    // 就在這個rootSaga裏面利用takeEvery去監聽action的type
    yield takeEvery('HOME_AD_DATA', getHomeAdData);
    yield takeEvery('GET_LIKE_LIST_DATA', getLikeListData);
}
//經過yield call Effect 獲取返回值,繼續下面操做
export function* getHomeAdData() {
    let data = yield call(getAdData)
    ...
    yield put({type:UPDATE_HOME_AD_DATA, data: dataArr})
}

export function getAdData() {
    const result = axios.get('/api/homead')
    return result
}
複製代碼
相關文章
相關標籤/搜索