国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

redux-saga 初識和使用

2019-11-19 14:11:54
字體:
供稿:網(wǎng)友

redux-saga 是一個管理 Redux 應(yīng)用異步操作的中間件,功能類似redux-thunk + async/await, 它通過創(chuàng)建 Sagas 將所有的異步操作邏輯存放在一個地方進(jìn)行集中處理。

redux-saga 的 effects

redux-saga中的 Effects 是一個純文本 JavaScript 對象,包含一些將被 saga middleware 執(zhí)行的指令。這些指令所執(zhí)行的操作包括如下三種:

  1. 發(fā)起一個異步調(diào)用(如發(fā)一起一個 Ajax 請求)
  2. 發(fā)起其他的 action 從而更新 Store
  3. 調(diào)用其他的 Sagas

Effects 中包含的指令有很多,具體可以異步API 參考進(jìn)行查閱

redux-saga 的特點

方便測試,例如:

assert.deepEqual(iterator.next().value, call(Api.fetch, '/products'))
  1. action 可以保持其純凈性,異步操作集中在 saga 中進(jìn)行處理
  2. watch/worker(監(jiān)聽->執(zhí)行) 的工作形式
  3. 被實現(xiàn)為 generator
  4. 對含有復(fù)雜異步邏輯的應(yīng)用場景支持良好
  5. 更細(xì)粒度地實現(xiàn)異步邏輯,從而使流程更加清晰明了,遇到 bug 易于追蹤和解決。
  6. 以同步的方式書寫異步邏輯,更符合人的思維邏輯
  7. 從 redux-thunk 到 redux-saga

假如現(xiàn)在有一個場景:用戶在登錄的時候需要驗證用戶的 username 和 password 是否符合要求。

使用 redux-thunk 實現(xiàn)

獲取用戶數(shù)據(jù)的邏輯(user.js):

// user.jsimport request from 'axios';// define constants// define initial state// export default reducerexport 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 });  }}

驗證登錄的邏輯(login.js):

import request from 'axios';import { loadUserData } from './user';export const login = (user, pass) => async (dispatch) => {  try {    dispatch({ type: LOGIN_REQUEST });    let { data } = await request.post('/login', { user, pass });    await dispatch(loadUserData(data.uid));    dispatch({ type: LOGIN_SUCCESS, data });  } catch(error) {    dispatch({ type: LOGIN_ERROR, error });  }}

redux-saga

異步邏輯可以全部寫進(jìn) saga.js 中:

export function* loginSaga() { while(true) {  const { user, pass } = yield take(LOGIN_REQUEST) //等待 Store 上指定的 action LOGIN_REQUEST  try {   let { data } = yield call(loginRequest, { user, pass }); //阻塞,請求后臺數(shù)據(jù)   yield fork(loadUserData, data.uid); //非阻塞執(zhí)行l(wèi)oadUserData   yield put({ type: LOGIN_SUCCESS, data }); //發(fā)起一個action,類似于dispatch  } catch(error) {   yield put({ type: LOGIN_ERROR, error });  }  }}export function* loadUserData(uid) { try {  yield put({ type: USERDATA_REQUEST });  let { data } = yield call(userRequest, `/users/${uid}`);  yield put({ type: USERDATA_SUCCESS, data }); } catch(error) {  yield put({ type: USERDATA_ERROR, error }); }}

難點解讀

對于 redux-saga, 還是有很多比較難以理解和晦澀的地方,下面筆者針對自己覺得比較容易混淆的概念進(jìn)行整理:

take 的使用

take 和 takeEvery 都是監(jiān)聽某個 action, 但是兩者的作用卻不一致,takeEvery 是每次 action 觸發(fā)的時候都響應(yīng),而 take 則是執(zhí)行流執(zhí)行到 take 語句時才響應(yīng)。takeEvery 只是監(jiān)聽 action, 并執(zhí)行相對應(yīng)的處理函數(shù),對何時執(zhí)行 action 以及如何響應(yīng) action 并沒有多大的控制權(quán),被調(diào)用的任務(wù)無法控制何時被調(diào)用,并且它們也無法控制何時停止監(jiān)聽,它只能在每次 action 被匹配時一遍又一遍地被調(diào)用。但是 take 可以在 generator 函數(shù)中決定何時響應(yīng)一個 action 以及 響應(yīng)后的后續(xù)操作。
例如在監(jiān)聽所有類型的 action 觸發(fā)時進(jìn)行 logger 操作,使用 takeEvery 實現(xiàn)如下:

import { takeEvery } from 'redux-saga'function* watchAndLog(getState) { yield* takeEvery('*', function* logger(action) {   //do some logger operation //在回調(diào)函數(shù)體內(nèi) })}

使用 take 實現(xiàn)如下:

import { take } from 'redux-saga/effects'function* watchAndLog(getState) { while(true) {  const action = yield take('*')  //do some logger operation //與 take 并行  })}

其中 while(true) 的意思是一旦到達(dá)流程最后一步(logger),通過等待一個新的任意的 action 來啟動一個新的迭代(logger 流程)。

阻塞和非阻塞

call 操作是用來發(fā)起異步操作的,對于 generator 來說,call 是阻塞的操作,它在 Generator 調(diào)用結(jié)束之前不能執(zhí)行或處理任何其他事情。,但是 fork 卻是非阻塞操作,當(dāng) fork 調(diào)動任務(wù)時,該任務(wù)會在后臺執(zhí)行,此時的執(zhí)行流可以繼續(xù)往后面執(zhí)行而不用等待結(jié)果返回。

例如如下的登錄場景:

function* loginFlow() { while(true) {  const {user, password} = yield take('LOGIN_REQUEST')  const token = yield call(authorize, user, password)  if(token) {   yield call(Api.storeItem({token}))   yield take('LOGOUT')   yield call(Api.clearItem('token'))  } }}

若在 call 在去請求 authorize 時,結(jié)果未返回,但是此時用戶又觸發(fā)了 LOGOUT 的 action,此時的 LOGOUT 將會被忽略而不被處理,因為 loginFlow 在 authorize 中被堵塞了,沒有執(zhí)行到 take('LOGOUT')那里

同時執(zhí)行多個任務(wù)

如若遇到某個場景需要同一時間執(zhí)行多個任務(wù),比如 請求 users 數(shù)據(jù) 和 products 數(shù)據(jù), 應(yīng)該使用如下的方式:

import { call } from 'redux-saga/effects'//同步執(zhí)行const [users, products] = yield [ call(fetch, '/users'), call(fetch, '/products')]//而不是//順序執(zhí)行const users = yield call(fetch, '/users'),   products = yield call(fetch, '/products')

當(dāng) yield 后面是一個數(shù)組時,那么數(shù)組里面的操作將按照 Promise.all 的執(zhí)行規(guī)則來執(zhí)行,genertor 會阻塞知道所有的 effects 被執(zhí)行完成

源碼解讀

在每一個使用 redux-saga 的項目中,主文件中都會有如下一段將 sagas 中間件加入到 Store 的邏輯:

const sagaMiddleware = createSagaMiddleware({sagaMonitor})const store = createStore( reducer, applyMiddleware(sagaMiddleware))sagaMiddleware.run(rootSaga)

其中 createSagaMiddleware 是 redux-saga 核心源碼文件 src/middleware.js 中導(dǎo)出的方法:

export default function sagaMiddlewareFactory({ context = {}, ...options } = {}) { ...  function sagaMiddleware({ getState, dispatch }) {  const channel = stdChannel()  channel.put = (options.emitter || identity)(channel.put)  sagaMiddleware.run = runSaga.bind(null, {   context,   channel,   dispatch,   getState,   sagaMonitor,   logger,   onError,   effectMiddlewares,  })  return next => action => {   if (sagaMonitor && sagaMonitor.actionDispatched) {    sagaMonitor.actionDispatched(action)   }   const result = next(action) // hit reducers   channel.put(action)   return result  } } ...  }

這段邏輯主要是執(zhí)行了 sagaMiddleware(),該函數(shù)里面將 runSaga 賦值給 sagaMiddleware.run 并執(zhí)行,最后返回 middleware。 接著看 runSaga() 的邏輯:

export function runSaga(options, saga, ...args) {... const task = proc(  iterator,  channel,  wrapSagaDispatch(dispatch),  getState,  context,  { sagaMonitor, logger, onError, middleware },  effectId,  saga.name, ) if (sagaMonitor) {  sagaMonitor.effectResolved(effectId, task) } return task}

這個函數(shù)里定義了返回了一個 task 對象,該 task 是由 proc 產(chǎn)生的,移步 proc.js:

export default function proc( iterator, stdChannel, dispatch = noop, getState = noop, parentContext = {}, options = {}, parentEffectId = 0, name = 'anonymous', cont,) { ... const task = newTask(parentEffectId, name, iterator, cont) const mainTask = { name, cancel: cancelMain, isRunning: true } const taskQueue = forkQueue(name, mainTask, end)  ...  next()  return task function next(arg, isErr){ ...   if (!result.done) {    digestEffect(result.value, parentEffectId, '', next)   }  ... }}

其中 digestEffect 就執(zhí)行了 effectTriggerd() 和 runEffect(),也就是執(zhí)行 effect,其中 runEffect() 中定義了不同 effect 執(zhí)行相對應(yīng)的函數(shù),每一個 effect 函數(shù)都在 proc.js 實現(xiàn)了。

除了一些核心方法之外,redux-saga 還提供了一系列的 helper 文件,這些文件的作用是返回一個類 iterator 的對象,便于后續(xù)的遍歷和執(zhí)行, 在此不具體分析。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 邵阳县| 马龙县| 体育| 巴林右旗| 山阴县| 青浦区| 武功县| 正蓝旗| 将乐县| 建水县| 视频| 上虞市| 上栗县| 苍南县| 泌阳县| 依兰县| 榕江县| 平阳县| 道真| 普宁市| 乌拉特后旗| 仁布县| 邵武市| 岐山县| 石林| 长海县| 二连浩特市| 桃园市| 故城县| 太原市| 庆城县| 惠州市| 长沙市| 来宾市| 天等县| 翼城县| 汝城县| 礼泉县| 丹寨县| 云阳县| 绥德县|