← Назад к вопросам
Как измеряется стандарт языка JavaScript?
1.3 Junior🔥 71 комментариев
#JavaScript Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Функции-генераторы в Redux Saga
Функции-генераторы (generator functions) — это специальный механизм JavaScript, используемый в Redux Saga для управления асинхронными операциями и сложной логикой побочных эффектов.
Что такое генераторы
Генератор — это функция, которая может прерваться и возобновить свое выполнение. Определяется с помощью function* и использует yield:
function* simpleGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = simpleGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
Генераторы в Redux Saga
Redux Saga использует генераторы для написания асинхронного кода в синхронном стиле:
import { put, call, takeEvery } from 'redux-saga/effects';
import * as API from './api';
// Saga генератор
function* fetchUserSaga(action) {
try {
// call эффект — выполняет асинхронную функцию
const user = yield call(API.fetchUser, action.payload.id);
// put эффект — диспетчит action
yield put({
type: 'FETCH_USER_SUCCESS',
payload: user
});
} catch (error) {
yield put({
type: 'FETCH_USER_ERROR',
payload: error.message
});
}
}
// Корневая saga, которая слушает actions
function* watchUserSaga() {
yield takeEvery('FETCH_USER_REQUEST', fetchUserSaga);
}
Основные эффекты Redux Saga
// call — вызвать функцию и ждать результата
const data = yield call(fetch, '/api/users');
// put — диспетчить action в store
yield put({ type: 'ACTION_NAME', payload: data });
// select — получить состояние из store
const users = yield select(state => state.users);
// takeEvery — слушать каждый action
yield takeEvery('ACTION_TYPE', saga);
// takeLatest — слушать последний action (отменяет предыдущие)
yield takeLatest('SEARCH_REQUEST', searchSaga);
// fork — запустить saga без ожидания
yield fork(backgroundSaga);
// all — запустить несколько саг параллельно
yield all([fork(saga1), fork(saga2)]);
// delay — задержка
yield delay(1000);
Практический пример: Загрузка данных
function* fetchPostsSaga(action) {
yield put({ type: 'FETCH_POSTS_START' });
try {
// Вызов API
const posts = yield call(
fetch,
`https://api.example.com/posts?page=${action.payload}`
).then(res => res.json());
// Успешно получили данные
yield put({
type: 'FETCH_POSTS_SUCCESS',
payload: posts
});
} catch (error) {
// Обработка ошибки
yield put({
type: 'FETCH_POSTS_ERROR',
payload: error.message
});
}
}
function* watchFetchPostsSaga() {
yield takeLatest('FETCH_POSTS_REQUEST', fetchPostsSaga);
}
Сложный пример: Обработка несколько действий
function* loginSaga(action) {
const { email, password } = action.payload;
try {
// Показать загрузку
yield put({ type: 'LOGIN_START' });
// Запрос на сервер
const response = yield call(API.login, email, password);
// Сохранить token в localStorage
localStorage.setItem('token', response.token);
// Диспетчить успех
yield put({
type: 'LOGIN_SUCCESS',
payload: response.user
});
// Перейти на главную страницу
yield call(navigateTo, '/dashboard');
} catch (error) {
yield put({
type: 'LOGIN_ERROR',
payload: error.message
});
}
}
function* watchLoginSaga() {
yield takeEvery('LOGIN_REQUEST', loginSaga);
}
Преимущества генераторов в Saga
- Синхронный стиль кода — асинхронность скрыта за
yield
// Вместо callback hell
fetch(...).then(res =>
res.json().then(data =>
dispatch(success(data))
)
);
// Используем удобный синхронный стиль
const data = yield call(fetch, ...);
yield put(success(data));
- Легко тестировать — каждый
yieldвозвращает effect объект
it('should fetch user', () => {
const gen = fetchUserSaga({ payload: { id: 1 } });
expect(gen.next().value)
.toEqual(call(API.fetchUser, 1));
});
-
Обработка ошибок через try/catch
-
Отмена операций — через механизм отмены саг
Отмена саг
function* watchSearchSaga() {
// takeLatest отменяет предыдущую сагу если пришла новая
yield takeLatest('SEARCH_REQUEST', searchSaga);
}
function* searchSaga(action) {
try {
// Эта операция будет отменена если придет новый SEARCH_REQUEST
const results = yield call(API.search, action.payload);
yield put({ type: 'SEARCH_SUCCESS', payload: results });
} catch (error) {
if (error.isCancelled) {
console.log('Search was cancelled');
}
}
}
Современные альтернативы
Хотя Redux Saga популярна, есть альтернативы:
- Redux Thunk — проще, но менее мощная
- Redux Observable — использует RxJS
- RTK Query — встроен в Redux Toolkit
- TanStack Query — универсальное решение для data fetching
// Пример с современным Redux Toolkit
const fetchUserSlice = createAsyncThunk(
'users/fetchUser',
async (userId) => {
const response = await fetch(`/api/users/${userId}`);
return response.json();
}
);
Почему использовать Saga
- Сложная логика побочных эффектов — саги справляются лучше
- Интеграция с Redux — всё в одном store
- Тестируемость — effects легко mock'ировать
- Предсказуемость — весь поток данных в Redux
Итог: функции-генераторы в Redux Saga предоставляют элегантный способ управления асинхронными операциями в Redux приложениях, хотя сегодня есть более современные альтернативы.