Redux-saga学习笔记

概述javascript

Redux-saga在Redux应用中扮演’中间件’的角色,主要用来执行数据流中的异步操做。主要经过ES6中的generator函数和yield关键字来以同步的方式实现异步操做。java

基本用法:redux

  1. 使用createSagaMiddleware方法建立saga 的Middleware,而后在建立的redux的store时,使用applyMiddleware函数将建立的saga Middleware实例绑定到store上,最后能够调用saga Middleware的run函数来执行某个或者某些Middleware。
  2. 在saga的Middleware中,可使用takeEvery或者takeLatest等API来监听某个action,当某个action触发后,saga可使用call、fetch等api发起异步操做,操做完成后使用put函数触发action,同步更新state,从而完成整个State的更新。

 

APIapi

  1. takeEvery

     用来监听action,每一个action都触发一次,若是其对应是异步操做的话,每次都发起异步请求,而不论上次的请求是否返回。promise

1
2
3
4
5
import  { takeEvery } from  'redux-saga/effects'
 
function * watchFetchData() {
   yield  takeEvery( 'FETCH_REQUESTED' , fetchData)
}

  

  1. takeLatest

    做用同takeEvery同样,惟一的区别是它只关注最后,也就是最近一次发起的异步请求,若是上次请求还未返回,则会被取消。缓存

1
2
3
function * watchFetchData() {
   yield  takeLatest( 'FETCH_REQUESTED' , fetchData)
}

  

  1. call

   call用来调用异步函数,将异步函数和函数参数做为call函数的参数传入,返回一个js对象。saga引入他的主要做用是方便测试,同时也能让咱们的代码更加规范化。app

同js原生的call同样,call函数也能够指定this对象,只要把this对象当第一个参数传入call方法就行了异步

saga一样提供apply函数,做用同call同样,参数形式同js原生apply方法。 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import  { call } from  'redux-saga/effects'
 
function * fetchProducts() {
   const products =  yield  call(Api.fetch,  '/products' )
   // ...
}
 
import  { call } from  'redux-saga/effects'
import  Api from  '...'
 
const iterator = fetchProducts()
 
// expects a call instruction
assert.deepEqual(
   iterator.next().value,
   call(Api.fetch,  '/products' ),
   "fetchProducts should yield an Effect call(Api.fetch, './products')"
)
 
yield  call([obj, obj.method], arg1, arg2, ...)
yield  apply(obj, obj.method, [arg1, arg2, ...])

  

  1. cps

    同call方法基本同样,可是用处不太同样,call通常用来完成异步操做,cps能够用来完成耗时比较长的io操做等。单元测试

  1. put

   put是saga对Redux中dispatch方法的一个封装,调用put方法后,saga内部会分发action通知Store更新state。

这个借口主要也是为了方便咱们写单元测试提供的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import  { call, put } from  'redux-saga/effects'
// ...
 
function * fetchProducts() {
   const products =  yield  call(Api.fetch,  '/products' )
   // create and yield a dispatch Effect
   yield  put({ type:  'PRODUCTS_RECEIVED' , products })
}
 
const products = {}
 
// expects a dispatch instruction
assert.deepEqual(
   iterator.next(products).value,
   put({ type:  'PRODUCTS_RECEIVED' , products }),
   "fetchProducts should yield an Effect put({ type: 'PRODUCTS_RECEIVED', products })"
)

  

  1. 请求失败

有两种方式来处理请求失败的状况,一种是使用try-catch方法,将错误抛出;另外一种是使用变量缓存成功失败的状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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 })
   }
}
 
 
import  Api from  './path/to/api'
import  { call, put } from  'redux-saga/effects'
 
function  fetchProductsApi() {
   return  Api.fetch( '/products' )
     .then(response => ({ response }))
     . catch (error => ({ error }))
}
 
function * fetchProducts() {
   const { response, error } =  yield  call(fetchProductsApi)
   if  (response)
     yield  put({ type:  'PRODUCTS_RECEIVED' , products: response })
   else
     yield  put({ type:  'PRODUCTS_REQUEST_FAILED' , error })
}

  

  1. take

  take的表现同takeEvery同样,都是监听某个action,但与takeEvery不一样的是,他不是每次action触发的时候都相应,而只是在执行顺序执行到take语句时才会相应action。

当在genetator中使用take语句等待action时,generator被阻塞,等待action被分发,而后继续往下执行。

takeEvery只是监听每一个action,而后执行处理函数。对于什么时候相应action和 如何相应action,takeEvery并无控制权。

而take则不同,咱们能够在generator函数中决定什么时候相应一个action,以及一个action被触发后作什么操做。

最大区别:take只有在执行流达到时才会响应对应的action,而takeEvery则一经注册,都会响应action。

1
2
3
4
5
6
7
8
import  { take, put } from  'redux-saga/effects'
 
function * watchFirstThreeTodosCreation() {
   for  ( let  i = 0; i < 3; i++) {
     const action =  yield  take( 'TODO_CREATED' )
   }
   yield  put({type:  'SHOW_CONGRATULATION' })
}

  

  1. fork

  非阻塞任务调用机制:上面咱们介绍过call能够用来发起异步操做,可是相对于generator函数来讲,call操做是阻塞的,只有等promise回来后才能继续执行,而fork是非阻塞的 ,当调用fork启动一个任务时,该任务在后台继续执行,从而使得咱们的执行流能继续往下执行而没必要必定要等待返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import  { take, call, put, cancelled } from  'redux-saga/effects'
import  Api from  '...'
 
function * authorize(user, password) {
   try  {
     const token =  yield  call(Api.authorize, user, password)
     yield  put({type:  'LOGIN_SUCCESS' , token})
     yield  call(Api.storeItem, {token})
     return  token
   catch (error) {
     yield  put({type:  'LOGIN_ERROR' , error})
   } finally {
     if  ( yield  cancelled()) {
       // ... put special cancellation handling code here
     }
   }
}

  

  1. cancel

cancel的做用是用来取消一个还未返回的fork任务。防止fork的任务等待时间太长或者其余逻辑错误。

  • all

   all提供了一种并行执行异步请求的方式。以前介绍过执行异步请求的api中,大都是阻塞执行,只有当一个call操做放回后,才能执行下一个call操做, call提供了一种相似Promise中的all操做,能够将多个异步操做做为参数参入all函数中,若是有一个call操做失败或者全部call操做都成功返回,则本次all操做执行完毕。

1
2
3
4
5
6
7
import  { all, call } from  'redux-saga/effects'
 
// correct, effects will get executed in parallel
const [users, repos]  =  yield  all([
   call(fetch,  '/users' ),
   call(fetch,  '/repos' )
])

  

  • race

   有时候当咱们并行的发起多个异步操做时,咱们并不必定须要等待全部操做完成,而只须要有一个操做完成就能够继续执行流。这就是race借口的用处。他能够并行的启动多个异步请求,只要有一个 请求返回(resolved或者reject),race操做接受正常返回的请求,而且将剩余的请求取消。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import  { race, take, put } from  'redux-saga/effects'
 
function * backgroundTask() {
   while  ( true ) { ... }
}
 
function * watchStartBackgroundTask() {
   while  ( true ) {
     yield  take( 'START_BACKGROUND_TASK' )
     yield  race({
       task: call(backgroundTask),
       cancel: take( 'CANCEL_TASK' )
     })
   }
}

  

  • actionChannel  

  在以前的操做中,全部的action分发是顺序的,可是对action的响应是由异步任务来完成,也便是说对action的处理是无序的。
  若是须要对action的有序处理的话,可使用actionChannel函数来建立一个action的缓存队列,但一个action的任务流程处理完成后,才但是执行下一个任务流。
  代码参考:

    

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import  { take, actionChannel, call, ... } from  'redux-saga/effects'
 
function * watchRequests() {
   // 1- Create a channel for request actions
   const requestChan =  yield  actionChannel( 'REQUEST' )
   while  ( true ) {
     // 2- take from the channel
     const {payload} =  yield  take(requestChan)
     // 3- Note that we're using a blocking call
     yield  call(handleRequest, payload)
   }
}
 
function * handleRequest(payload) { ... }
  •   eventChannel
  • Throttling

用来防止接二连三的响应某个事件。    

1
2
3
4
5
6
7
8
9
import  { throttle } from  'redux-saga/effects'
 
function * handleInput(input) {
   // ...
}
 
function * watchInput() {
   yield  throttle(500,  'INPUT_CHANGED' , handleInput)
}

  

  • Debouncing

延时执行,使用delay函数实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import  { delay } from  'redux-saga'
 
function * handleInput(input) {
   // debounce by 500ms
   yield  call(delay, 500)
   ...
}
 
function * watchInput() {
   let  task
   while  ( true ) {
     const { input } =  yield  take( 'INPUT_CHANGED' )
     if  (task) {
       yield  cancel(task)
     }
     task =  yield  fork(handleInput, input)
   }
}
 
 
const delay = (ms) =>  new  Promise(resolve => setTimeout(resolve, ms))

  参考:https://redux-saga.js.org/docs/api/

相关文章
相关标签/搜索