如何在重复触发的事件监听中仅执行一次函数?(语音合成场景)
解决文本转语音(TTS)应用重复朗读的问题
问题根源
你之前的布尔变量executed定义在keyup事件的回调函数内部,每次触发keyup事件都会重新创建这个变量并赋值为false,所以每次判断!executed都是真,导致语音合成被重复调用,布尔值完全起不到控制作用。
解决方案:防抖+朗读状态控制
要实现「输入后延迟0.5-1秒自动朗读,且不重复触发」的需求,需要结合防抖(延迟执行)和朗读状态标记来处理,代码示例如下:
// 在事件外部定义变量,避免每次触发重置 let debounceTimer = null; let isSpeaking = false; const speech = new SpeechSynthesisUtterance(); document.querySelector("textarea").addEventListener("keyup", e => { // 每次按键时清除之前的定时器,确保只有停止输入后才触发 if (debounceTimer) { clearTimeout(debounceTimer); } const inputText = e.target.value.trim(); // 输入为空时,停止当前朗读并返回 if (!inputText) { window.speechSynthesis.cancel(); isSpeaking = false; return; } // 设置延迟1秒执行朗读(可改为500即0.5秒) debounceTimer = setTimeout(() => { // 如果当前正在朗读,先终止之前的朗读 if (isSpeaking) { window.speechSynthesis.cancel(); } speech.text = inputText; window.speechSynthesis.speak(speech); isSpeaking = true; // 朗读结束后重置状态标记 speech.onend = () => { isSpeaking = false; }; }, 1000); });
代码说明
- 防抖定时器
debounceTimer:每次用户按键时,清除上一次的定时器,重新计时。只有当用户停止输入达到设定的延迟时间(1秒/0.5秒),才会执行朗读操作,避免频繁触发。 - 朗读状态标记
isSpeaking:记录当前是否正在朗读,触发新的朗读前先终止之前的朗读,确保始终朗读最新的输入内容,同时在朗读结束后重置标记。 - 空文本处理:当输入框内容为空时,主动停止朗读,避免无意义的语音输出。
内容的提问来源于stack exchange,提问作者SAKSoomro
相关产品推荐
相关产品推荐

