Redux-saga在Redux应用中扮演’中间件’的角色,主要用来执行数据流中的异步操作。主要通过ES6中的generator函数和yield关键字来以同步的方式实现异步操作。

基本用法:

  • 使用createSagaMiddleware方法创建saga 的Middleware,然后在创建的redux的store时,使用applyMiddleware函数将创建的saga Middleware实例绑定到store上,最后可以调用saga Middleware的run函数来执行某个或者某些Middleware。
  • 在saga的Middleware中,可以使用takeEvery或者takeLatest等API来监听某个action,当某个action触发后,saga可以使用call、fetch等api发起异步操作,操作完成后使用put函数触发action,同步更新state,从而完成整个State的更新。
  • takeEvery
  • 用来监听action,每个action都触发一次,如果其对应是异步操作的话,每次都发起异步请求,而不论上次的请求是否返回。

    call用来调用异步函数,将异步函数和函数参数作为call函数的参数传入,返回一个js对象。saga引入他的主要作用是方便测试,同时也能让我们的代码更加规范化。

    同js原生的call一样,call函数也可以指定this对象,只要把this对象当第一个参数传入call方法就好了

    saga同样提供apply函数,作用同call一样,参数形式同js原生apply方法。

    function * fetchProducts() {
    const products = yield call(Api.fetch, '/products' )
    // ...
    import { call } from 'redux-saga/effects'
    import Api from '...'
    const iterator = fetchProducts()
    // expects a call instruction
    assert.deepEqual(
    iterator.next().value,
    call(Api.fetch, '/products' ),
    "fetchProducts should yield an Effect call(Api.fetch, './products')"
    yield call([obj, obj.method], arg1, arg2, ...)
    yield apply(obj, obj.method, [arg1, arg2, ...])
    function * fetchProducts() {
    const products = yield call(Api.fetch, '/products' )
    // create and yield a dispatch Effect
    yield put({ type: 'PRODUCTS_RECEIVED' , products })
    const products = {}
    // expects a dispatch instruction
    assert.deepEqual(
    iterator.next(products).value,
    put({ type: 'PRODUCTS_RECEIVED' , products }),
    "fetchProducts should yield an Effect put({ type: 'PRODUCTS_RECEIVED', products })"
    function * fetchProducts() {
    try {
    const products = yield call(Api.fetch, '/products' )
    yield put({ type: 'PRODUCTS_RECEIVED' , products })
    catch (error) {
    yield put({ type: 'PRODUCTS_REQUEST_FAILED' , error })
    import Api from './path/to/api'
    import { call, put } from 'redux-saga/effects'
    function fetchProductsApi() {
    return Api.fetch( '/products' )
    .then(response => ({ response }))
    . catch (error => ({ error }))
    function * fetchProducts() {
    const { response, error } = yield call(fetchProductsApi)
    if (response)
    yield put({ type: 'PRODUCTS_RECEIVED' , products: response })
    yield put({ type: 'PRODUCTS_REQUEST_FAILED' , error })

    take的表现同takeEvery一样,都是监听某个action,但与takeEvery不同的是,他不是每次action触发的时候都相应,而只是在执行顺序执行到take语句时才会相应action。

    当在genetator中使用take语句等待action时,generator被阻塞,等待action被分发,然后继续往下执行。

    takeEvery只是监听每个action,然后执行处理函数。对于何时相应action和 如何相应action,takeEvery并没有控制权。

    而take则不一样,我们可以在generator函数中决定何时相应一个action,以及一个action被触发后做什么操作。

    最大区别:take只有在执行流达到时才会响应对应的action,而takeEvery则一经注册,都会响应action。

    import { take, put } from 'redux-saga/effects'
    function * watchFirstThreeTodosCreation() {
    for ( let i = 0; i < 3; i++) {
    const action = yield take( 'TODO_CREATED' )
    yield put({type: 'SHOW_CONGRATULATION' })
    import { take, call, put, cancelled } from 'redux-saga/effects'
    import Api from '...'
    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 { all, call } from 'redux-saga/effects'
    // correct, effects will get executed in parallel
    const [users, repos]  = yield all([
    call(fetch, '/users' ),
    call(fetch, '/repos' )
    function * watchStartBackgroundTask() {
    while ( true ) {
    yield take( 'START_BACKGROUND_TASK' )
    yield race({
    task: call(backgroundTask),
    cancel: take( 'CANCEL_TASK' )
    import { take, actionChannel, call, ... } from 'redux-saga/effects'
    function * watchRequests() {
    // 1- Create a channel for request actions
    const requestChan = yield actionChannel( 'REQUEST' )
    while ( true ) {
    // 2- take from the channel
    const {payload} = yield take(requestChan)
    // 3- Note that we're using a blocking call
    yield call(handleRequest, payload)
    function * handleRequest(payload) { ... }