Redux Saga 嵌套生成器函数效果不会使用 yield put(打字稿)按顺序运行
Redux Saga nested Generator function effects don't runs in sequence using yield put (typescript)
我想执行两个异步操作,当两个操作都完成处理后,再执行第三个操作。为此,我创建了三个 saga worker:第一个更新 DB 上的电子邮件字段:
export function* emailUpdateRequestSaga(action: IEmailUpdateRequest) {
const requestURL = '/updateEmail';
const requestData = {
userId: action.userId,
email: action.email
};
try {
const {data, status}: Pick<AxiosResponse, 'data' | 'status'> = yield call(
update,
requestURL,
requestData
);
yield put(emailUpdateSuccess({data, status}));
} catch (err) {
console.log('err', err);
yield put(emailUpdateFail(err));
}
}
第二个发送邮件:
export function* genericEmailRequestSaga(action: IGenericEmailRequest) {
const requestURL = '/sendEmail';
const requestOpt = {
headers: {},
body: {
email: action.email
}
};
try {
const {data, status}: Pick<AxiosResponse, 'data' | 'status'> = yield call(
post,
requestURL,
requestOpt
);
yield put(genericEmailSuccess({data, status}));
} catch (err) {
console.log('err', err);
yield put(genericEmailFail(err));
}
}
然后,第三个包起来放成功动作:
export function* emailSendAndUpdateRequestSaga(action: IEmailSendAndUpdateRequest) {
try {
// first generator
yield put(emailUpdateRequest(action.userId, action.email));
// second generator
yield put(genericEmailRequest(action.email));
// success action
yield put(emailSendAndUpdateSuccess(true));
} catch (err) {
console.log('err', err);
yield put(emailSendAndUpdateFail(err));
}
}
这是观察者:
export function* sagas() {
yield takeEvery(EmailActionEnum.SEND_EMAIL_REQUEST, genericEmailRequestSaga);
yield takeEvery(EmailActionEnum.EMAIL_UPDATE_REQUEST, emailUpdateRequestSaga);
yield takeEvery(EmailActionEnum.EMAIL_SEND_AND_UPDATE_REQUEST, emailSendAndUpdateRequestSaga);
}
问题是,在 emailSendAndUpdateRequestSaga
中,yield put(emailSendAndUpdateSuccess(true));
在之前的请求启动后被触发,但即使它们失败了,我仍然会触发成功操作。
我只想在前面的生成器无误地完成处理后才触发第三个动作,我该怎么做?
// first generator
yield put(emailUpdateRequest(action.userId, action.email));
// second generator
yield put(genericEmailRequest(action.email));
虽然这些行确实间接导致其他 sagas 发生 运行,但它们直接做的唯一事情就是派发一个动作。分派一个动作是同步的,所以这段代码在继续之前根本不会等待。
如果您想完全按照当前的方式保留这两个 saga,那么您可以使用 take
来监听这些 saga 最终将派发的操作,以便暂停您的主要 saga。例如:
export function* emailSendAndUpdateRequestSaga(action: IEmailSendAndUpdateRequest) {
try {
yield put(emailUpdateRequest(action.userId, action.email));
const action = yield take([
// I'm guessing the action types are constants something like this, but they weren't in your sample code.
EmailActionEnum.EMAIL_UPDATE_SUCCESS,
EmailActionEnum.EMAIL_UPDATE_FAIL
]);
if (action.type === EmailActionEnum.EMAIL_UPDATE_FAIL) {
throw action;
}
yield put(genericEmailRequest(action.email));
const action = yield take([
EmailActionEnum.SEND_EMAIL_SUCCESS,
EmailActionEnum.SEND_EMAIL_FAIL
]);
if (action.type === EmailActionEnum.SEND_EMAIL_FAIL) {
throw action;
}
// success action
yield put(emailSendAndUpdateSuccess(true));
} catch (err) {
console.log('err', err);
yield put(emailSendAndUpdateFail(err));
}
}
我认为更好的解决方案是改变您的方法,而不是分派操作,而是直接调用 sagas。结合修改 sagas 以便它们在出现错误时抛出,您可以执行以下操作:
export function* emailUpdateRequestSaga(action: IEmailUpdateRequest) {
const requestURL = '/updateEmail';
const requestData = {
userId: action.userId,
email: action.email
};
try {
const {data, status}: Pick<AxiosResponse, 'data' | 'status'> = yield call(
update,
requestURL,
requestData
);
yield put(emailUpdateSuccess({data, status}));
} catch (err) {
yield put(emailUpdateFail(err));
throw err; // <---- added this to rethrow the error
}
}
export function* genericEmailRequestSaga(action: IGenericEmailRequest) {
// ... code omitted. Add a throw like in emailUpdateRequestSaga
}
export function* emailSendAndUpdateRequestSaga(action: IEmailSendAndUpdateRequest) {
try {
// Changed to `call` instead of `put`
yield call(emailUpdateRequestSaga, emailUpdateRequest(action.userId, action.email));
// Changed to `call` instead of `put`
yield call(genericEmailRequestSaga, genericEmailRequest(action.email));
// success action
yield put(emailSendAndUpdateSuccess(true));
} catch (err) {
console.log('err', err);
yield put(emailSendAndUpdateFail(err));
}
}
yield call(/*etc*/)
将 运行 指定的 saga 直到它完成。主要传奇在 returns 或抛出之前不会继续。
我想执行两个异步操作,当两个操作都完成处理后,再执行第三个操作。为此,我创建了三个 saga worker:第一个更新 DB 上的电子邮件字段:
export function* emailUpdateRequestSaga(action: IEmailUpdateRequest) {
const requestURL = '/updateEmail';
const requestData = {
userId: action.userId,
email: action.email
};
try {
const {data, status}: Pick<AxiosResponse, 'data' | 'status'> = yield call(
update,
requestURL,
requestData
);
yield put(emailUpdateSuccess({data, status}));
} catch (err) {
console.log('err', err);
yield put(emailUpdateFail(err));
}
}
第二个发送邮件:
export function* genericEmailRequestSaga(action: IGenericEmailRequest) {
const requestURL = '/sendEmail';
const requestOpt = {
headers: {},
body: {
email: action.email
}
};
try {
const {data, status}: Pick<AxiosResponse, 'data' | 'status'> = yield call(
post,
requestURL,
requestOpt
);
yield put(genericEmailSuccess({data, status}));
} catch (err) {
console.log('err', err);
yield put(genericEmailFail(err));
}
}
然后,第三个包起来放成功动作:
export function* emailSendAndUpdateRequestSaga(action: IEmailSendAndUpdateRequest) {
try {
// first generator
yield put(emailUpdateRequest(action.userId, action.email));
// second generator
yield put(genericEmailRequest(action.email));
// success action
yield put(emailSendAndUpdateSuccess(true));
} catch (err) {
console.log('err', err);
yield put(emailSendAndUpdateFail(err));
}
}
这是观察者:
export function* sagas() {
yield takeEvery(EmailActionEnum.SEND_EMAIL_REQUEST, genericEmailRequestSaga);
yield takeEvery(EmailActionEnum.EMAIL_UPDATE_REQUEST, emailUpdateRequestSaga);
yield takeEvery(EmailActionEnum.EMAIL_SEND_AND_UPDATE_REQUEST, emailSendAndUpdateRequestSaga);
}
问题是,在 emailSendAndUpdateRequestSaga
中,yield put(emailSendAndUpdateSuccess(true));
在之前的请求启动后被触发,但即使它们失败了,我仍然会触发成功操作。
我只想在前面的生成器无误地完成处理后才触发第三个动作,我该怎么做?
// first generator
yield put(emailUpdateRequest(action.userId, action.email));
// second generator
yield put(genericEmailRequest(action.email));
虽然这些行确实间接导致其他 sagas 发生 运行,但它们直接做的唯一事情就是派发一个动作。分派一个动作是同步的,所以这段代码在继续之前根本不会等待。
如果您想完全按照当前的方式保留这两个 saga,那么您可以使用 take
来监听这些 saga 最终将派发的操作,以便暂停您的主要 saga。例如:
export function* emailSendAndUpdateRequestSaga(action: IEmailSendAndUpdateRequest) {
try {
yield put(emailUpdateRequest(action.userId, action.email));
const action = yield take([
// I'm guessing the action types are constants something like this, but they weren't in your sample code.
EmailActionEnum.EMAIL_UPDATE_SUCCESS,
EmailActionEnum.EMAIL_UPDATE_FAIL
]);
if (action.type === EmailActionEnum.EMAIL_UPDATE_FAIL) {
throw action;
}
yield put(genericEmailRequest(action.email));
const action = yield take([
EmailActionEnum.SEND_EMAIL_SUCCESS,
EmailActionEnum.SEND_EMAIL_FAIL
]);
if (action.type === EmailActionEnum.SEND_EMAIL_FAIL) {
throw action;
}
// success action
yield put(emailSendAndUpdateSuccess(true));
} catch (err) {
console.log('err', err);
yield put(emailSendAndUpdateFail(err));
}
}
我认为更好的解决方案是改变您的方法,而不是分派操作,而是直接调用 sagas。结合修改 sagas 以便它们在出现错误时抛出,您可以执行以下操作:
export function* emailUpdateRequestSaga(action: IEmailUpdateRequest) {
const requestURL = '/updateEmail';
const requestData = {
userId: action.userId,
email: action.email
};
try {
const {data, status}: Pick<AxiosResponse, 'data' | 'status'> = yield call(
update,
requestURL,
requestData
);
yield put(emailUpdateSuccess({data, status}));
} catch (err) {
yield put(emailUpdateFail(err));
throw err; // <---- added this to rethrow the error
}
}
export function* genericEmailRequestSaga(action: IGenericEmailRequest) {
// ... code omitted. Add a throw like in emailUpdateRequestSaga
}
export function* emailSendAndUpdateRequestSaga(action: IEmailSendAndUpdateRequest) {
try {
// Changed to `call` instead of `put`
yield call(emailUpdateRequestSaga, emailUpdateRequest(action.userId, action.email));
// Changed to `call` instead of `put`
yield call(genericEmailRequestSaga, genericEmailRequest(action.email));
// success action
yield put(emailSendAndUpdateSuccess(true));
} catch (err) {
console.log('err', err);
yield put(emailSendAndUpdateFail(err));
}
}
yield call(/*etc*/)
将 运行 指定的 saga 直到它完成。主要传奇在 returns 或抛出之前不会继续。