admin管理员组

文章数量:1289845

If i have two sagas waiting for a yield take() on the same action, is there a guarantee which saga will pick up the action first and execute its remaining logic or is random? I need to ensure the first saga executes its logic before the second saga does.

function* rootSaga() {
    yield [ 
        saga1(),
        saga2()
    ]
}

function* saga1() {
    while (true) {
        //Random logic
        yield take("MY_ACTION")
        //Finish executing logic
    }
}

function* saga2() {
    while (true) {
        //Random logic
        yield take("MY_ACTION")
        //Finish executing logic
    }
}

If i have two sagas waiting for a yield take() on the same action, is there a guarantee which saga will pick up the action first and execute its remaining logic or is random? I need to ensure the first saga executes its logic before the second saga does.

function* rootSaga() {
    yield [ 
        saga1(),
        saga2()
    ]
}

function* saga1() {
    while (true) {
        //Random logic
        yield take("MY_ACTION")
        //Finish executing logic
    }
}

function* saga2() {
    while (true) {
        //Random logic
        yield take("MY_ACTION")
        //Finish executing logic
    }
}
Share Improve this question edited Oct 28, 2017 at 20:20 Gabriel West asked Oct 28, 2017 at 19:58 Gabriel WestGabriel West 9412 gold badges12 silver badges25 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 6

You cannot rely here on the order of execution. When your action is dispatched, all sagas that have yielded a take effect matching the action will be resumed at once. If an order of execution was "guaranteed", it would be an implementation detail you shouldn't rely on.

If you need your saga2 to be resumed after saga1 has executed the logic subsequent to "MY_ACTION", what your saga2 should really be waiting for is a different action indicating that saga1 has finished its job, rather than the first one.

function* rootSaga() {
  yield [ 
    saga1(),
    saga2()
  ]
}

function* saga1() {
  while (true) {
    //Random logic
    yield take("MY_ACTION")
    //Finish executing logic
    yield put({type: "MY_ACTION_DONE"})
  }
}

function* saga2() {
  while (true) {
    //Random logic
    yield take("MY_ACTION_DONE")
    //Finish executing logic
  }
}

Rather than having both run independently, you can call or fork the dependent generator from the initial one after the necessary logic has been performed.

Note there is a subtle but important difference between call and fork. call(saga2) is blocking so would pause your while loop and not react to any more "MY_ACTION" actions until saga2 has pleted too, whereas fork(saga2) is non-blocking, acting like a background task so would continue to execute in parallel to your loop resuming, so you could continue to respond to further "MY_ACTION".

Another thing to note is that fork is still attached to the parent saga, so will be cancelled along with it, errors will bubble to parent from it etc. I imagine this is desirable but if you need a pletely detached version that will continue to run no matter what happens to parent saga, use spawn instead

function* rootSaga() {
    yield all([ 
        fork(saga1)
    ])
}

function* saga1() {
    while (true) {
        //Random logic
        const action = yield take("MY_ACTION")
        //Finish executing logic

        // BLOCKING `call` saga2, passing original action as param if needed
        yield call(saga2, action)

        // OR NON-BLOCKING `fork` saga2
        yield fork(saga2, action)

        // OR NON-BLOCKING DETACHED `spawn` saga2
        yield spawn(saga2, action)
    }
}

function* saga2(action) {
    // Do whatever dependant logic
    // safe in the knowledge that saga1 has done its job already
}

本文标签: javascriptReduxSaga Is there a guaranteed order of saga executionStack Overflow