天天看點

使用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