Redux Saga:如何等待可能未执行的save动作完成后再执行其他操作
问题描述
我有一个实体包含三个动作:save、submit和delete。表单值变化时会触发save动作调用API,日常使用中每隔几秒就会触发一次。为避免数据不一致,我用action channel和滑动buffer实现了类似takeLatest的效果,代码如下:
const saveBuffer = buffers.sliding(1); function* saveWatcher() { const saveChannel = yield actionChannel('SAVE', saveBuffer); while (true) { const payload = yield take(saveChannel); yield call(save, payload); } }
save函数示例:
function* save() { try { yield delay(5000); yield put(actions.saveSuccess()); } catch (e) { yield put(actions.saveError()); } }
现在需要实现:触发delete/submit动作时,必须确保当前没有正在执行的save任务。我试过检查buffer是否为空,但这不可行——buffer为空时save可能还在运行;另外也存在从未调用过save的情况(比如未修改内容就提交)。
我尝试过以下代码:
function* waitForPendingSaves() { const hasPendingSaves = !saveBuffer.isEmpty(); if (hasPendingSaves) { yield race([ take(constants.SAVE_SUCCESS), take(constants.SAVE_ERROR), ]); } }
但问题在于,当缓冲项出队处理时,save执行中途可能会触发delete。我考虑过设置一个状态标记,处理SAVE事件时开启,SAVE_SUCCESS/ERROR完成后关闭,但不确定怎么优雅实现。
注意:我希望等待save任务完成,不考虑取消任务。
解决方案
1. 维护save运行状态标记
在saveWatcher里添加一个全局状态变量,标记当前是否有save任务正在执行。用finally块确保状态一定会被重置,避免异常导致状态卡住:
let isSaveRunning = false; const saveBuffer = buffers.sliding(1); function* saveWatcher() { const saveChannel = yield actionChannel('SAVE', saveBuffer); while (true) { const payload = yield take(saveChannel); isSaveRunning = true; try { yield call(save, payload); } finally { isSaveRunning = false; } } }
2. 编写等待所有save完成的辅助函数
这个函数会循环检查状态,直到没有save在运行且buffer为空,确保所有待处理和正在执行的save都完成:
function* waitForAllSavesToComplete() { while (isSaveRunning || !saveBuffer.isEmpty()) { yield race([ take(constants.SAVE_SUCCESS), take(constants.SAVE_ERROR), ]); } }
3. 在submit/delete的saga中调用辅助函数
处理submit或delete动作时,先调用辅助函数等待所有save完成,再执行后续业务逻辑:
function* submitWatcher() { while (true) { yield take(constants.SUBMIT); yield call(waitForAllSavesToComplete); // 执行submit的业务逻辑 yield call(submitLogic); } } function* deleteWatcher() { while (true) { yield take(constants.DELETE); yield call(waitForAllSavesToComplete); // 执行delete的业务逻辑 yield call(deleteLogic); } }
核心优势
- 不需要修改原有
save函数的逻辑,侵入性低 - 覆盖了所有场景:
save正在运行、buffer有待处理任务、从未触发过save - 用
finally保证状态可靠,不会因为异常导致标记一直为true
内容的提问来源于stack exchange,提问作者plusheen
相关产品推荐
相关产品推荐

