最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

redux-saga 初識和使用

 更新時間:2018年03月10日 10:56:52   作者:grecep  
這篇文章主要介紹了redux-saga 初識和使,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

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

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 參考進行查閱

redux-saga 的特點

方便測試,例如:

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

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

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

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

// user.js

import request from 'axios';

// define constants
// define initial state
// export default reducer

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 });
  }
}

驗證登錄的邏輯(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

異步邏輯可以全部寫進 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, 還是有很多比較難以理解和晦澀的地方,下面筆者針對自己覺得比較容易混淆的概念進行整理:

take 的使用

take 和 takeEvery 都是監(jiān)聽某個 action, 但是兩者的作用卻不一致,takeEvery 是每次 action 觸發(fā)的時候都響應,而 take 則是執(zhí)行流執(zhí)行到 take 語句時才響應。takeEvery 只是監(jiān)聽 action, 并執(zhí)行相對應的處理函數(shù),對何時執(zhí)行 action 以及如何響應 action 并沒有多大的控制權(quán),被調(diào)用的任務無法控制何時被調(diào)用,并且它們也無法控制何時停止監(jiān)聽,它只能在每次 action 被匹配時一遍又一遍地被調(diào)用。但是 take 可以在 generator 函數(shù)中決定何時響應一個 action 以及 響應后的后續(xù)操作。
例如在監(jiān)聽所有類型的 action 觸發(fā)時進行 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) 的意思是一旦到達流程最后一步(logger),通過等待一個新的任意的 action 來啟動一個新的迭代(logger 流程)。

阻塞和非阻塞

call 操作是用來發(fā)起異步操作的,對于 generator 來說,call 是阻塞的操作,它在 Generator 調(diào)用結(jié)束之前不能執(zhí)行或處理任何其他事情。,但是 fork 卻是非阻塞操作,當 fork 調(diào)動任務時,該任務會在后臺執(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í)行多個任務

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

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')

當 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 中導出的方法:

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í)行相對應的函數(shù),每一個 effect 函數(shù)都在 proc.js 實現(xiàn)了。

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

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 一文了解你不知道的JavaScript閉包篇

    一文了解你不知道的JavaScript閉包篇

    這篇文章主要為大家詳細介紹了一些你不知道的JavaScript閉包相關(guān)知識,文中的示例代碼講解詳細,對我們學習JavaScript有一定幫助,感興趣的可以跟隨小編一起學習一下
    2022-11-11
  • 用JS得到字符串中出現(xiàn)次數(shù)最多的字母

    用JS得到字符串中出現(xiàn)次數(shù)最多的字母

    一個字符串String=“adadfdfseffserfefsefseetsdg”,找出里面出現(xiàn)次數(shù)最多的字母和出現(xiàn)的次數(shù)。 經(jīng)典的解答思路為:取出字符串的第一個字符(首字母),利用的字符串的 replace 方法將符合正則表達式(第一個字符)替代為空,此字母出現(xiàn)的次數(shù)為原始的字符串長度減去替代后的字符串長度。循環(huán)迭代找出長度最長的字母。
    2008-03-03
  • BootStrap 動態(tài)添加驗證項和取消驗證項的實現(xiàn)方法

    BootStrap 動態(tài)添加驗證項和取消驗證項的實現(xiàn)方法

    這篇文章主要介紹了BootStrap 動態(tài)添加驗證項和取消驗證項的實現(xiàn)方法的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-09-09
  • JavaScript實現(xiàn)動態(tài)時鐘效果

    JavaScript實現(xiàn)動態(tài)時鐘效果

    這篇文章主要介紹了JavaScript實現(xiàn)動態(tài)時鐘效果,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下,希望對你的學習有所幫助
    2022-06-06
  • js parseInt的陷阱分析小結(jié)

    js parseInt的陷阱分析小結(jié)

    js parseInt的陷阱分析小結(jié),當?shù)谝粋€字符為0時,Js會把它看成一個8進制數(shù)字,其他8進制之外的字符都回被忽略掉。
    2011-03-03
  • javascript中錯誤使用var造成undefined

    javascript中錯誤使用var造成undefined

    這篇文章主要介紹了javascript中錯誤使用var造成undefined的原因,實例分析了錯誤使用var造成undefined的過程,感興趣的小伙伴們可以參考一下
    2016-03-03
  • JS遍歷ul下的li點擊彈出li的索引的實現(xiàn)方法

    JS遍歷ul下的li點擊彈出li的索引的實現(xiàn)方法

    這篇文章主要介紹了JS遍歷ul下的li點擊彈出li的索引的方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-09-09
  • 微信小程序scroll-x失效的完美解決方法

    微信小程序scroll-x失效的完美解決方法

    在微信小程序的文檔中,使用scroll-view標簽,然后給它設(shè)置一個scroll-x就可以實現(xiàn)元素,橫向排列,可以左右滑動,但是在實際開發(fā)中會遇到各種問題,下面小編給大家?guī)砹宋⑿判〕绦騭croll-x失效的完美解決方法,需要的朋友可以參考下
    2018-07-07
  • 前端實現(xiàn)文件下載的4種常見方式與實戰(zhàn)示例

    前端實現(xiàn)文件下載的4種常見方式與實戰(zhàn)示例

    在前端開發(fā)中,實現(xiàn)文件下載是一個非常常見的需求,本篇文章將介紹 4 種前端文件下載的方式,并提供詳細示例,希望能幫助大家掌握文件下載的各種技巧
    2025-04-04
  • 利用JavaScript做數(shù)獨的完整實現(xiàn)過程

    利用JavaScript做數(shù)獨的完整實現(xiàn)過程

    數(shù)獨游戲是在一個9*9的方格中進行填數(shù)字的游戲,需要滿足的規(guī)則是每行每列和每個子九宮格都是1~9的不重復數(shù)字,下面這篇文章主要給大家介紹了關(guān)于如何利用JavaScript做數(shù)獨的相關(guān)資料,需要的朋友可以參考下
    2021-09-09

最新評論

获嘉县| 鄂伦春自治旗| 茌平县| 潞西市| 乐平市| 漠河县| 抚顺县| 湾仔区| 济阳县| 喀喇| 分宜县| 保德县| 六安市| 瓮安县| 尉氏县| 沈丘县| 遂平县| 上虞市| 嘉祥县| 龙井市| 汝州市| 兰考县| 锡林浩特市| 乐昌市| 萍乡市| 香港 | 乐陵市| 彰化市| 若尔盖县| 星座| 涟源市| 综艺| 平顶山市| 维西| 富顺县| 栖霞市| 交口县| 禹州市| 福贡县| 奇台县| 昆山市|