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启发)

猜你喜欢

转载自www.cnblogs.com/fe-linjin/p/11137430.html