zoukankan      html  css  js  c++  java
  • Redux-Saga.js 学习笔记

    昨天认真看完了Promise 与 Generator,今天早上认真学了一下redux-saga.js。把笔记输出到这里

    redux-saga的api

    • put

      作用类似于 dispatch,用来 put 同步 的 generator function,也把一些状态 put 到 reducer

    • call

      我的理解是 调用api , 发起请求,也可以用来call 同步的 action

      官方说明

      与前面的例子不同的是,现在我们不立即执行异步调用,相反,call 创建了一条描述结果的信息。 就像在 Redux 里你使用 action 创建器,创建一个将被 Store 执行的、描述 action 的纯文本对象。 call 创建一个纯文本对象描述函数调用。redux-saga middleware 确保执行函数调用并在响应被 resolve 时恢复 generator。

      需要注意的,写异步流的时候

      // 错误写法,effects 将按照顺序执行
      const users = yield call(fetch, '/users'),
            repos = yield call(fetch, '/repos')
      
      // 由于第二个 effect 将会在第一个 call 执行完毕才开始。所以我们需要这样写:
      import { call } from 'redux-saga/effects'
      
      // 正确写法, effects 将会同步执行
      const [users, repos] = yield [
        call(fetch, '/users'),
        call(fetch, '/repos')
      ]
      
      
    • takeEvery

      非常常见,提供了类似 redux-thunk的行为

      // 首先我们创建一个将执行异步 action 的任务:
      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});
         }
      }
      // 然后在每次 FETCH_REQUESTED action 被发起时启动上面的任务。
      import { takeEvery } from 'redux-saga'
      
      function* watchFetchData() {
        yield* takeEvery('FETCH_REQUESTED', fetchData)
      }
      

      在上面的例子中,takeEvery 允许多个 fetchData 实例同时启动。在某个特定时刻,尽管之前还有一个或多个 fetchData 尚未结束,我们还是可以启动一个新的 fetchData 任务,

    • takeLatest

      如果我们只想得到最新那个请求的响应(例如,始终显示最新版本的数据)。我们可以使用 takeLatest 辅助函数。

      import { takeLatest } from 'redux-saga'
      
      function* watchFetchData() {
        yield* takeLatest('FETCH_REQUESTED', fetchData)
      }
      

      takeEvery 不同,在任何时刻 takeLatest 只允许一个 fetchData 任务在执行。并且这个任务是最后被启动的那个。 如果已经有一个任务在执行的时候启动另一个 fetchData ,那之前的这个任务会被自动取消。

    • select

      根据 在组件中dispatch一个action的例子中,如果要在effects中对于param数据和当前的state数据进行再出处理,这里怎么获取state呢?采用select,如下:

      export default {
       
        namespace: 'example',   
       
        state: {num:1},     //表示当前的example中的state状态,这里可以给初始值,这里num初始为1
       
       
        effects: { //这里是做异步处理的
          *addByONe({ param}, { call, put,select }) { //这里使用select
       
            const num = yield select(state => state.num)    //这里就获取到了当前state中的数据num
           //方式二: const num = yield select(({num}) =>num)
       
          //方式三: const num = yield select(_ =>_.num)
       
            let param1;
             param1 = num + param;   这里就可以使用num进行操作了
        
            yield put({
               type: 'save',   
               num:param1
            });
          }
         
       
        },
          
        //用来保存更新state值 上面的put方法调用这里的方法
        reducers: {
          save(state, action) { //这里的state是当前总的state,这里的action包含了上面传递的参数和type
            return { ...state, ...action.num }; //这里用ES6语法来更新当前state中num的值
          },
        },
       
      };
      
    • take

      take 就像我们更早之前看到的 callput。它创建另一个命令对象,告诉 middleware 等待一个特定的 action。 正如在 call Effect 的情况中,middleware 会暂停 Generator,直到返回的 Promise 被 resolve。 在 take 的情况中,它将会暂停 Generator 直到一个匹配的 action 被发起了。 在以上的例子中,watchAndLog 处于暂停状态,直到任意的一个 action 被发起。

      import { select, take } from 'redux-saga/effects'
      
      function* watchAndLog() {
        while (true) {
          const action = yield take('*')
          const state = yield select()
      
          console.log('action', action)
          console.log('state after', state)
        }
      }
      

      一个简单的例子,假设在我们的 Todo 应用中,我们希望监听用户的操作,并在用户初次创建完三条 Todo 信息时显示祝贺信息。

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

      使用拉取(pull)模式,我们可以在同一个地方写控制流,而不是重复处理相同的 action。

      function* loginFlow() {
        while (true) {
          yield take('LOGIN')
          // ... perform the login logic
          yield take('LOGOUT')
          // ... perform the logout logic
        }
      }
      
    • fork

      为了表示无阻塞调用,redux-saga 提供了另一个 Effect:fork。 当我们 fork 一个 任务,任务会在后台启动,调用者也可以继续它自己的流程,而不用等待被 fork 的任务结束。

      所以为了让 loginFlow 不错过一个并发的 LOGOUT,我们不应该使用 call 调用 authorize 任务,而应该使用 fork

      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
          }
        }
      }
      
    • cancel

      如果我们收到一个 LOGOUT action,我们将那个 task 传入给 cancel Effect。 如果任务仍在运行,它会被中止。如果任务已完成,那什么也不会发生,取消操作将会是一个空操作(no-op)。最后,如果该任务完成了但是有错误, 那我们什么也没做,因为我们知道,任务已经完成了。

      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
          }
        }
      }
      
    • race

      有时候我们同时启动多个任务,但又不想等待所有任务完成,我们只希望拿到 胜利者:即第一个被 resolve(或 reject)的任务。 race Effect 提供了一个方法,在多个 Effects 之间触发一个竞赛(race)。

      // 下面的示例演示了触发一个远程的获取请求,并且限制了 1 秒内响应,否则作超时处理。
      import { race, call, put } from 'redux-saga/effects'
      import { delay } from 'redux-saga'
      
      function* fetchPostsWithTimeout() {
        const {posts, timeout} = yield race({
          posts: call(fetchApi, '/posts'),
          timeout: call(delay, 1000)
        })
      
        if (posts)
          put({type: 'POSTS_RECEIVED', posts})
        else
          put({type: 'TIMEOUT_ERROR'})
      }
      

      race 的另一个有用的功能是,它会自动取消那些失败的 Effects。例如,假设我们有 2 个 UI 按钮:

      • 第一个用于在后台启动一个任务,这个任务运行在一个无限循环的 while(true) 中(例如:每 x 秒钟从服务器上同步一些数据)
      • 一旦该后台任务启动了,我们启用第二个按钮,这个按钮用于取消该任务。
      import { race, take, call } 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')
          })
        }
      }
      
  • 相关阅读:
    BZOJ 1040 (ZJOI 2008) 骑士
    BZOJ 1037 (ZJOI 2008) 生日聚会
    ZJOI 2006 物流运输 bzoj1003
    ZJOI 2006 物流运输 bzoj1003
    NOI2001 炮兵阵地 洛谷2704
    NOI2001 炮兵阵地 洛谷2704
    JLOI 2013 卡牌游戏 bzoj3191
    JLOI 2013 卡牌游戏 bzoj3191
    Noip 2012 day2t1 同余方程
    bzoj 1191 [HNOI2006]超级英雄Hero——二分图匹配
  • 原文地址:https://www.cnblogs.com/ssaylo/p/12808218.html
Copyright © 2011-2022 走看看