У меня есть ситуация, когда у меня есть функция (named onGetCameras
), которая принимает функцию обратного вызова (например, getCamerasSuccess
); намерение состоит в вызове onGetCameras
(которая является внешней функцией), которая выполняет вызов AJAX и после завершения вызывает getCamerasSuccess
, передавая полученный результат.
У меня есть что-то вроде этого:
// My 'actionCreators' variable is a reference to create my actions
function* getCamerasSaga(action: action.GetCameras) {
const { onGetCameras } = action;
const getCamerasSuccess = function*(response: GetCamerasResponse) {
console.log('SUCCESS', response);
yield put(actionCreators.getCamerasSuccess(response);
}
yield put(actionCreators.getCamerasStart());
yield call(onGetCameras, getCamerasSuccss);
}
export function* watchCamera() {
yield takeEvery(ActionTypes.GetCameras, getCamerasSaga);
}
Я не могу понять, почему это не getCamerasSuccess
в мою функцию getCamerasSuccess
: я никогда не вижу свое console.log
сообщение внутри этой функции
Но, если я изменю свой обратный вызов на успех, чтобы быть нормальной функцией, например:
const getCamerasSuccess = (response: GetCamerasResponse) => {
console.log('RESPONSE', response);
}
Я вижу, что получаю свой ответ, но, как я уже говорил, используя функцию генератора, кажется, что он никогда не входит внутрь этой функции.
Любая помощь глубоко ценится.
Ваш getCamerasSuccess
не вызывается, потому что когда вы вызываете функцию, которую он выполняет, но когда вы вызываете функцию генератора, он возвращает только объект-итератор, по которому вы должны продолжать звонить next
чтобы он выполнялся.
Ваш код все равно не будет работать, потому что вы пытаетесь использовать эффект саги в генераторе, который не находится под контролем сокращения-саги. Если вы хотите продолжать работать с обратными вызовами, вам может быть интересен эффект cps (https://redux-saga.js.org/docs/api/#cpsfn-args). Обратный вызов должен быть в стиле node.js, хотя (первый параметр err, второй результат).
Тогда ваш код может выглядеть следующим образом:
function* getCamerasSaga(action: action.GetCameras) {
const { onGetCameras } = action;
yield put(actionCreators.getCamerasStart());
try {
const response: GetCamerasResponse = yield cps(onGetCameras);
console.log('SUCCESS', response);
yield put(actionCreators.getCamerasSuccess(response));
} catch(err) { /* ... */ }
}
Если модификация API onGetCameras
не является вариантом, вам придется либо использовать обычную функцию в качестве обратного вызова, а затем использовать store.dispatch
вместо put
или вы можете создать небольшую служебную функцию с eventChannel (https://redux-saga.js. org/docs/advanced/Channels.html).
Например:
...
import { eventChannel, END } from 'redux-saga';
function createCbChannel(fn) {
return eventChannel(emitter => {
fn((response) => {
emitter(response);
emitter(END);
});
return () => emitter(END);
});
}
function* getCamerasSaga(action: action.GetCameras) {
const { onGetCameras } = action;
yield put(actionCreators.getCamerasStart());
const cbChannel = yield call(createCbChannel, onGetCameras);
const response: GetCamerasResponse = yield take(cbChannel);
console.log('SUCCESS', response);
yield put(actionCreators.getCamerasSuccess(response));
}
cps
кажется, делает трюк для меня. Только один вопрос: я использую TypeScript, поэтому какой должен быть типonGetCameras
потому что сейчас, когда я получаюyield cps(onGetCameras)
, он жалуется, чтоcontext
свойства отсутствует в типе, который я ему дал --- заставить это работать, я сделал это:yield cps(onGetCameras.bind(null))
, но я не уверен, что это правильный способ сделать этоinterface CpsCallback<R> { (error: any, result: R): void; cancel?(): void; }