You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Streamlit Cloud应用中JavaScript计时器页面跳转异常及NameError问题排查与解决方案咨询

Streamlit Cloud应用中JavaScript计时器页面跳转异常及NameError问题排查与解决方案咨询

嘿,我仔细梳理了你的问题和代码,咱们一步步拆解解决这些困扰你的问题:

一、JavaScript计时器自动页面跳转失效的原因与修复方案

你的JS计时器能正常显示时间,但无法触发跳转,核心问题在于你用fetch('/timeup-passage')的方式不对——Streamlit是单页应用,没有这类后端路由,而且即使发送了请求,Streamlit也不会自动重新运行(rerun)来检测状态变化。

修复方法:用JS修改URL Query参数触发Streamlit rerun

Streamlit会自动监听URL中query参数的变化并触发rerun,所以我们可以把JS里的fetch逻辑改成修改URL的query参数:

修改passage_write_step中的JS代码:

<script>
var timeLeft = {total_time};
var countdownElem = document.getElementById('countdown_passage');
var interval = setInterval(function(){
    timeLeft--;
    countdownElem.innerHTML = "Time left: " + timeLeft + " seconds";
    if(timeLeft <= 0){
        clearInterval(interval);
        // 替换fetch,改为修改URL query参数
        window.location.search = '?timeup-passage=1';
    }
}, 1000);
</script>

同样,email_write_step中的JS代码也做类似修改:

<script>
var timeLeftEmail = {total_time};
var countdownElemEmail = document.getElementById('countdown_email');
var intervalEmail = setInterval(function(){
    timeLeftEmail--;
    countdownElemEmail.innerHTML = "Time left: " + timeLeftEmail + " seconds";
    if(timeLeftEmail <= 0){
        clearInterval(intervalEmail);
        window.location.search = '?timeup-email=1';
    }
}, 1000);
</script>

这样当计时器归零时,URL会带上对应的query参数,Streamlit检测到后会自动rerun,执行你写的if st.query_params.get("timeup-passage"):逻辑。

二、NameError: 'passage_read_step'未定义的根源与解决

看你的代码,我发现了一个明显的拼写错误——在save_email_answer函数里,你写的是st.sessionstate.get,但正确的写法是st.session_state.get(少了下划线)!这个错误会导致调用该函数时触发NameError,因为st.sessionstate这个对象根本不存在。

修复这个函数:

def save_email_answer():
    post_to_external_api(st.session_state.get("email_answer", ""), "email")

另外,之前用st.experimental_get_query_params时出现的NameError,大概率是因为rerun时会话状态未正确初始化,导致代码尝试访问未定义的变量。现在你切换到st.query_params后,只要确保所有函数都在main()调用前定义(你的代码已经做到了),再配合上面的query参数触发rerun的方式,这个问题应该就能解决。

三、Streamlit与JavaScript处理定时事件和页面跳转的推荐模式

结合你的场景,我推荐以下几种可靠的交互模式:

  • URL Query参数触发状态变更:这是最简洁的方式,就像上面修复的那样,JS修改URL query参数,Streamlit监听st.query_params的变化来执行页面跳转或逻辑处理,适合简单的定时跳转场景。
  • 会话状态+JS计时器配合:用Streamlit的会话状态记录任务状态(比如submitted),JS计时器负责倒计时,时间到了通过修改query参数触发rerun,再由Python端根据会话状态和query参数执行下一步。
  • 避免使用st_autorefresh处理长定时st_autorefresh会强制页面频繁rerun,不仅会导致计时器冻结,还会消耗更多资源,对于超过10秒的定时任务,优先用JS计时器。
  • 复杂场景用自定义组件:如果需要更复杂的JS-Python双向通信(比如实时传递数据),可以用Streamlit Components开发自定义组件,但对于你的考试应用,前面两种模式已经足够。

额外的代码优化建议

  1. move_to_step函数中,修改query参数避免残留:比如跳转时可以清空之前的timeup参数,防止下次进入页面误触发逻辑:
def move_to_step(next_step):
    st.info(f"move_to_step() called with next_step: {next_step}")
    st.session_state.step = next_step
    st.session_state.start_time = time.time()
    st.session_state.submitted = False
    # 清空query参数
    st.query_params.clear()
    st.rerun()
  1. passage_read_step中,st_autorefresh会导致页面每秒rerun,建议换成JS计时器,避免页面频繁刷新影响体验。

备注:内容来源于stack exchange,提问作者dd d

火山引擎 最新活动