Составление Saga-ов
Хотя использование yield*
обеспечивает идиоматический способ составления Saga-ов, этот подход имеет некоторые ограничения:
Вероятно, вы захотите протестировать вложенные генераторы отдельно.Это приводит к некоторому дублированию в тестовом коде, а также приводит к накладным расходам на дублирующийся код. Мы не хотим выполнять вложенный генератор, а только хотим убедитесь, что он был вызван с правильным аргументом.
Что еще более важно,
yield*
принимает в качестве аргумента только последовательность тасков, поэтому вы можете передаватьyield*
только один генератор за раз.
Вы можете просто использовать yield
, чтобы параллельно запускать одну или несколько подзадач.
При вызове генератора, Saga будет ждать завершения работы генератора перед продолжением задачи, а затем возабновится с возвращенным значением (или выбрасывает исключение, если ошибка распространяется от подзадачи).
function* fetchPosts() {
yield put(actions.requestPosts())
const products = yield call(fetchApi, '/products')
yield put(actions.receivePosts(products))
}
function* watchFetch() {
while (yield take(FETCH_POSTS)) {
yield call(fetchPosts) // запускает и ожидает завершения таска fetchPosts
}
}
Yielding to an array of nested generators will start all the sub-generators in parallel, wait for them to finish, then resume with all the results
function* mainSaga(getState) {
const results = yield all([call(task1), call(task2), ...])
yield put(showResults(results))
}
На самом деле, yield Saga-ов не отличается от yield других Effect-ов (actions, таймауты, т.д.).
Например, вам может понадобавится, чтобы пользователь закончил игру в течение ограниченного периода времени:
function* game(getState) {
let finished
while (!finished) {
// есть всего 60 секунд что бы закончить игру
const {score, timeout} = yield race({
score: call(play, getState),
timeout: call(delay, 60000) // 60000 - 60 секунд
})
if (!timeout) {
finished = true
yield put(showScore(score))
}
}
}