使用react-saga来管理store中的effects

本文介绍了如何使用react-saga来管理store中的副作用,包括API接口的阻塞与非阻塞操作,如takeLatest确保只执行最后一个任务,取消fork任务,同时执行多个任务的race策略,以及在Generator中处理任务取消的finally区块。文章详细阐述了各种saga方法的用法,如take、put、call、fork等,并展示了在实际场景中的应用。

API接口的阻塞和非阻塞

/**
 * 同步call(阻塞,必须完成一个,才能继续下一个yield)
 * 异步fork(无阻塞)
 * 
 * 
 * 监听未来的action   take
 * 
 */

阻塞 / 非阻塞
名称 阻塞
takeEvery 否
takeLatest 否
takeLeading 否
throttle 否
take 是
take(channel) 有时 (请查看 API 参考)
take.maybe 是
put 否
put.resolve 是
put(channel, action) 否
call 是
apply 是
cps 是
fork 否
spawn 否
join 是
cancel 否
select 否
actionChannel 否
flush 是
cancelled 是
race 是
delay 是
all 当 array 或 object 中有阻塞型 effect 的时候阻塞。
// takeEvery并发任务的实现

import {fork, take} from "redux-saga/effects"

const takeEvery = (pattern, saga, ...args) => fork(function*() {
  while (true) {
    const action = yield take(pattern)
    yield fork(saga, ...args.concat(action))
  }
})
// takeEvery 可以让多个 saga 任务并行被 fork 执行

// takeLatest只执行最后一个任务

import {cancel, fork, take} from "redux-saga/effects"

const takeLatest = (pattern, saga, ...args) => fork(function*() {
  let lastTask
  while (true) {
    const action = yield take(pattern)
    if (lastTask) {
      yield cancel(lastTask) // 如果任务已经结束,则 cancel 为空操作
    }
    lastTask = yield fork(saga, ...args.concat(action))
  }
})

// takeLatest 不允许多个 saga 任务并行地执行。一旦接收到新的发起的 action,它就会取消前面所有 fork 过的任务(如果这些任务还在执行的话)。
// 在处理 AJAX 请求的时候,如果我们只希望获取最后那个请求的响应,takeLatest 就会非常有用。

// /取消fork异步任务

function* loginFlow() {
    while (true) {
        const { user, password } = yield take('LOGIN_REQUEST')
        // fork return a Task object
        const task = yield fork(authorize, user, password)
        const action = yield take(['LOGOUT', 'LOGIN_ERROR'])
        if (action.type === 'LOGOUT')
            yield cancel(task)
        yield call(Api.clearItem('token'))
    }
}

// cancel Effect 不会粗暴地结束我们的 authorize 任务,相反它会给予一个机会执行清理的逻辑。
// 在 finally 区块可以处理任何的取消逻辑(以及其他类型的完成逻辑)。
// 由于 finally 区块执行在任何类型的完成上(正常的 return, 错误, 或强制取消),如果你想要为取消作特殊处理,有一个 cancelled

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

// 同时执行多个任务

import { call } from 'redux-saga/effects'

// 正确写法, effects 将会同步执行
const [users, repos] = yield[
    call(fetch, '/users'),
    call(fetch, '/repos')
]

// 当我们需要 yield 一个包含 effects 的数组, generator 会被阻塞直到所有的 effects 都执行完毕,或者当一个 effect 被拒绝 (就像 Promise.all 的行为)

//在多个 Effects 之间启动 race,有时候我们同时启动多个任务,但又不想等待所有任务完成,我们只希望拿到 胜利者:即第一个被 resolve(或 reject)的任务。
// race 的另一个有用的功能是,它会自动取消那些失败的 Effects

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

// 组合组合 Sagas方法使用

// 你可能希望用户在有限的时间内完成一些游戏

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

//取消一个任务 Generator 跳进 finally 区块

// 除了手动取消任务,还有一些情况的取消是自动触发的。

// 在 race Effect 中。所有参与 race 的任务,除了优胜者(译注:最先完成的任务),其他任务都会被取消。

// 并行的 Effect (yield […])。一旦其中任何一个任务被拒绝,并行的 Effect 将会被拒绝(受 Promise.all 启发)。在这种情况中,所有其他的 Effect 将被自动取消。

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 cancel(task) 不会等待被取消的任务完成(即执行其 catch 区块)Effect 的行为和 fork 有点类似。 一旦取消发起,它就会尽快返回。一旦取消,任务通常应尽快完成它的清理逻辑然后返回。
            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)
    }
}

欢迎star本人github:https://github.com/flyku

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值