本文详解 jspsych 中如何正确实现“达到指定正确次数后自动结束试验”的逻辑,重点剖析变量作用域、状态持久化及 loop_function 的正确用法,避免因局部变量重置导致的无限循环。
本文详解 jspsych 中如何正确实现“达到指定正确次数后自动结束试验”的逻辑,重点剖析变量作用域、状态持久化及 loop_function 的正确用法,避免因局部变量重置导致的无限循环。
在 JsPsych 中,通过 loop_function 控制试验流程是常见需求,例如“连续答对 10 道数学题后退出”。但初学者常因变量作用域错误或状态未跨 trial 持久化,导致循环无法终止——表面看代码逻辑合理,实则 correctCount 或 acc 每次都被重新初始化为 0,使 loop_function 始终返回 true,陷入无限循环。
? 核心原则:状态变量必须在循环外部声明且全局可访问
JsPsych 的 on_finish 回调函数和 loop_function 是独立执行的上下文。若将计数器(如 correctCount)定义在 on_finish 内部(如第二版代码中的 var acc = 0),它每次仅在单次 trial 结束时存在,随后即被销毁,无法累积历史状态。正确做法是:将计数器声明在 jsPsych.run() 调用前的外层作用域,确保所有 trial 共享同一变量实例。
✅ 正确实现示例(修正版)
以下代码实现了「随机呈现数学题 → 实时校验答案 → 累计 10 次正确后终止」的完整逻辑:
<title>Math Quiz - Accuracy-Based Loop</title><script src="jspsych/dist/jspsych.js"></script><script src="jspsych/dist/plugin-survey-text.js"></script><link href="jspsych/dist/jspsych.css" rel="stylesheet"><script>
var jsPsych = initJsPsych();
// ✅ 正确:计数器声明在全局作用域(循环外部)
var correctCount = 0;
// 数学题库:结构化存储题目与答案
var mathQuestions = [
{ math: '2 + 2 = ?', correct: '4' },
{ math: '3 + 7 = ?', correct: '10' },
{ math: 'How many weeks in a year?', correct: '52' }
];
// 单次答题 trial
var testTrial = {
type: jsPsychSurveyText,
questions: [{
prompt: jsPsych.timelineVariable('math'),
name: 'resp',
required: true
}],
post_trial_gap: 500,
on_finish: function(data) {
// ✅ 正确:从 timelineVariable 获取预设答案,并与用户输入比对(忽略空格和大小写)
var userResponse = (data.response.resp || '').trim();
var expectedAnswer = jsPsych.timelineVariable('correct');
// 简单字符串匹配(生产环境建议增强校验逻辑)
if (userResponse === expectedAnswer) {
data.correct = true;
correctCount++; // ✅ 累加全局计数器
console.log(`✅ Correct! Total so far: ${correctCount}`);
} else {
data.correct = false;
console.log(`❌ Incorrect. Still need ${10 - correctCount} more.`);
}
}
};
// 循环节点:使用 timeline_variables 随机轮询题库
var mathLoopNode = {
timeline: [testTrial],
timeline_variables: mathQuestions,
sample: {
type: 'with-replacement'
}, // 随机抽样(允许重复)
loop_function: function(data) {
// ✅ 正确:直接读取全局 correctCount,判断是否达标
if (correctCount >= 10) {
console.log("? Target reached! Ending task.");
return false; // 终止循环
} else {
return true; // 继续下一轮
}
}
};
// 启动实验
jsPsych.run([mathLoopNode]);
</script>
⚠️ 关键注意事项
- 避免在 on_finish 中声明计数器:如 var acc = 0 必须移出函数体,否则每次都是新变量;
- timeline_variables + sample 是推荐模式:比手动 randomization.sampleWithReplacement 更符合 JsPsych 数据流设计,且天然支持 jsPsych.timelineVariable() 动态绑定;
- 答案校验需健壮:示例中使用 .trim() 处理首尾空格;实际项目中建议添加数字解析(如 parseInt())、单位忽略(如 "52 weeks" → "52")等逻辑;
- 调试技巧:在 on_finish 和 loop_function 中加入 console.log(),实时观察 correctCount 变化,快速定位状态更新是否生效;
- 重置需求? 若需多轮实验(如练习+正式测试),应在每轮开始前显式重置 correctCount = 0。
? 总结
JsPsych 的循环控制本质是状态驱动:loop_function 的返回值取决于一个能跨 trial 持久存在的变量。牢记“计数器生命周期必须长于单次 trial”,即可避开 90% 的无限循环陷阱。掌握此模式后,还可轻松扩展为「正确率 ≥ 80%」「最长尝试 20 次」等更复杂的终止条件。











