我有一个 Saga 创建了一个setTimeout的嵌套来通过axios发送消息,如下所示(为此我在saga中调用了一个非生成器函数):
import { call, put, takeLatest } from "redux-saga/effects";
import * as Action from "./action";
import axios from "axios";
import { SEND_MESSAGES } from "./constant";
function setTimeoutMessage(messageInfo) {
let index = 0;
setTimeout(function(){tick(index, messageInfo)}, messageInfo[index].delay);
}
function tick(index, messageInfo) {
// send message to server
axios.post("http://127.0.0.1:3000/send", null, {
params: {
ip: messageInfo[index].ip,
port: messageInfo[index].port,
message: messageInfo[index].message
}
})
++index;
if (index < messageInfo.length) {
setTimeout(function(){tick(index, messageInfo)}, messageInfo[index].delay);
}
}
function* sendMessages(action) {
try {
// send message to server
yield call(setTimeoutMessage, action.messageInfos)
yield put(Action.sendMessageSuccess());
yield put(Action.setIsRunning(false));
console.log("end try") //I've also try with 'yield console.log("end try")'
} catch (error) {
yield put(Action.sendMessageFailed(error));
yield put(Action.setIsRunning(false));
console.log("end catch") //also try with 'yield console.log("end catch")'
}
}
function* defaultSaga() {
yield takeLatest(SEND_MESSAGES, sendMessages);
}
export default defaultSaga;
在运行时, Saga 执行yield call(setTimeoutMessage, action.messageInfos)
,但在执行非生成器函数的setTimeout之前,执行saga的以下操作(console.log()
和yield put
)。
为了解决这个问题,我尝试如下实现yield race
,但是得到的结果和以前一样,分配给race的常量都是未定义的(即使我确定要调度CANCEL
操作)
import { call, put, takeLatest, race, take } from "redux-saga/effects";
import * as Action from "./action";
import axios from "axios";
import { SEND_MESSAGES, CANCEL } from "./constant";
function setTimeoutMessage(messageInfo) {
console.log('enter non generator function')
let index = 0;
setTimeout(function(){tick(index, messageInfo)}, messageInfo[index].delay);
}
function tick(index, messageInfo) {
console.log('timeout stuff')
// send message to server
axios.post("http://127.0.0.1:3000/send", null, {
params: {
ip: messageInfo[index].ip,
port: messageInfo[index].port,
message: messageInfo[index].message
}
})
++index;
if (index < messageInfo.length) {
setTimeout(function(){tick(index, messageInfo)}, messageInfo[index].delay);
}
}
function* sendMessages(action) {
const { task, cancel } = yield race({
task: call(setTimeoutMessage, action.messageInfos),
cancel: take(CANCEL)
})
console.log("after race", task, cancel)
if (cancel) {
let id = window.setTimeout(function() {}, 0);
while (id--) {
window.clearTimeout(id);
}
} else if (task) {
yield put(Action.sendMessageSuccess());
} else {
console.log("in if condition nothing ")
yield put(Action.sendMessageFailed());
}
console.log("after yield race + check what effect succeed ")
yield put(Action.setIsRunning(false));
}
function* defaultSaga() {
yield takeLatest(SEND_MESSAGES, sendMessages);
}
export default defaultSaga;
第二个实现在控制台中给出了以下结果:
enter non generator function
after race undefined undefined
in if condition nothing
timeout stuff
我的 Saga 函数末尾的console.log("after yield race + check what effect succeed ")
从未显示
如果让任务发生,或者从一开始就调度CANCEL
操作,则会得到相同的结果
是什么导致了这种行为?如何强制 Saga 在执行saga中的其余指令之前先执行yield call
?
PS:我不使用redux-toolkit,因为我使用相对较老版本的redux向现有代码添加特性
1条答案
按热度按时间cedebl8k1#
你没有给出或返回任何承诺,生成器应该等待。将你的函数重构为一个saga,并使用
delay
saga效果等待下一个tick: