天天看点

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

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