redux 如果在生成器函数内部调用非生成器函数,生成器函数不会暂停执行

vd2z7a6w  于 2023-03-08  发布在  其他
关注(0)|答案(1)|浏览(160)

我有一个 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向现有代码添加特性

cedebl8k

cedebl8k1#

你没有给出或返回任何承诺,生成器应该等待。将你的函数重构为一个saga,并使用delay saga效果等待下一个tick:

const postApiMessage = params =>
  axios.post("http://127.0.0.1:3000/send", null, { params });

function* setTimeoutMessage(messageInfo) {
  let index = 0;

  while (index < messageInfo.length) {
    const { delay: timeout, ip, port, message } = messageInfo[index];

    yield call(postApiMessage, { ip, port, message });

    index++;

    yield delay(timeout);
  }
}
    • 用法:**
function* sendMessages(action) {
  try {
    // send message to server
    yield 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")'
  }
}

相关问题