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之間重複觸發,形成死循環 

相關文章
相關標籤/搜索