• redux-saga学习进阶篇二


    今日学习目录

    一、race进一步讨论

    二、组合saga

    三、取消任务caceled进一步讨论

    一、race进一步讨论

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

    // 1. 触发一个远程的获取请求,并且限制了 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'})
    }
    
    
    // 2. race 的另一个有用的功能是,它会自动取消那些失败的 Effects。
    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')
        })
      }
    }
    
    

    二、 组合Sagas

    虽然使用 yield* 是提供组合 Sagas 的惯用方式,但这个方法也有一些局限性

    • 你可能会想要单独测试嵌套的 Generator。这导致了一些重复的测试代码及重复执行的开销。 我们不希望执行一个嵌套的 Generator,而仅仅是想确认它是被传入正确的参数来调用。

    • 更重要的是,yield* 只允许任务的顺序组合,所以一次你只能 yield* 一个 Generator。

    // 1. 直接使用 yield 来并行地启动一个或多个子任务
    // 当 yield 一个 call 至 Generator,Saga 需要等待 Generator 处理结束, 
    // 然后以返回的值恢复执行(或错误从子任务中传播过来,则抛出异常)
    function* fetchPosts() {
      yield put( actions.requestPosts() )
      const products = yield call(fetchApi, '/products')
      yield put( actions.receivePosts(products) )
    }
    
    function* watchFetch() {
      while ( yield take(FETCH_POSTS) ) {
        yield call(fetchPosts) // 等待 fetchPosts 完成
      }
    }
    
    
    // 2. yield 一个队列的嵌套的 Generators,将同时启动这些子 Generators(sub-generators),
    // 并等待它们完成。 然后以所有返回的结果恢复执行:
    function* mainSaga(getState) {
      const results = yield [call(task1), call(task2), ...]
      yield put(showResults(results))
    }
    
    
    // 3. 使用 effect 合并器将那些 Sagas 和所有其他类型的 Effect 合并。
    function* game(getState) {
      let finished
      while(!finished) {
        // 必须在 60 秒内完成
        const {score, timeout} = yield race({
          score: call( play, getState),
          timeout: call(delay, 60000)
        })
    
        if (!timeout) {
          finished = true
          yield put(showScore(score))
        }
      }
    }
    
    

    三、取消任务进一步讨论

    . 一旦任务被 fork,可以使用 yield cancel(task) 来中止任务执行。取消正在运行的任务

    example:

    一个可通过某些 UI 命令启动或停止的后台同步任务。 在接收到 START_BACKGROUND_SYNC action 后,我们 fork 一个后台任务,周期性地从远程服务器同步一些数

    这个任务将会一直执行直到一个 STOP_BACKGROUND_SYNC action 被触发。 然后我们取消后台任务,等待下一个 START_BACKGROUND_SYNC action。

    import { take, put, call, fork, cancel, cancelled, delay } from 'redux-saga/effects'
    import { someApi, actions } from 'somewhere'
    
    function* bgSync() {
      try {
        while (true) {
          yield put(actions.requestStart())
          const result = yield call(someApi)
          yield put(actions.requestSuccess(result))
          yield delay(5000)
        }
      } finally {
        if (yield cancelled())
          yield put(actions.requestFailure('Sync cancelled!'))
      }
    }
    
    function* main() {
      while ( yield take(START_BACKGROUND_SYNC) ) {
        // 启动后台任务
        const bgSyncTask = yield fork(bgSync)
    
        // 等待用户的停止操作
        yield take(STOP_BACKGROUND_SYNC)
        // 用户点击了停止,取消后台任务
        // 这会导致被 fork 的 bgSync 任务跳进它的 finally 区块
        yield cancel(bgSyncTask)
      }
    }
    // 取消 bgSyncTask 将会导致 Generator 跳进 finally 区块。
    // 可使用 yield cancelled() 来检查 Generator 是否已经被取消。
    
    

    2. 取消正在执行的任务,也将同时取消被阻塞在当前 Effect 中的任务。

    举个例子,假设在应用程序生命周期的某个时刻,还有挂起的(未完成的)调用链:

    function* main() {
      const task = yield fork(subtask)
      ...
      // later
      yield cancel(task)
    }
    
    function* subtask() {
      ...
      yield call(subtask2) // currently blocked on this call
      ...
    }
    
    function* subtask2() {
      ...
      yield call(someApi) // currently blocked on this call
      ...
    }
    // yield cancel(task) 触发了 subtask 任务的取消,反过来它将触发 subtask2 的取消。
    
    

    3. 关于自动取消

    除了上述的一些手动取消外,还存在一些自动取消的案例

    • 在race Effect中,除了最先完成任务的,其他都会被取消
    • 并行的Effect( yield [...] ),一旦其中的任何一个任务被拒绝了,并行的Effect将会被拒绝(受Promise.all启发)
  • 相关阅读:
    bzoj 1054
    bzoj 1047
    bzoj 2761
    bzoj 1191
    bzoj 2748
    bzoj_1003 物流运输
    新的开始( [USACO08OCT]打井Watering Hole)
    map
    Generic Cow Protests-G——60分做法
    逆序对
  • 原文地址:https://www.cnblogs.com/fe-linjin/p/11137430.html
Copyright © 2020-2023  润新知