【问题标题】:Recursive function not terminating even when break condition is met - JavaScript即使满足中断条件,递归函数也不会终止 - JavaScript
【发布时间】:2021-04-24 15:32:18
【问题描述】:

我正在构建一个 React.JS 组件,单击按钮时,它会从 JokeAPI 获取一个笑话,并使用 Web Synthesis 文本转语音 API 对其进行叙述。当通过全局布尔变量按下stop 按钮时,我想停止这种叙述和获取笑话。但是,该函数并没有停止并继续递归执行。

JokeComponent.js

export default function JokeComponent() {
  const [jokeState, jokeStateSetter] = useState();
  var RUNNING

  const fetchAndNarrate = () => {
    if(RUNNING){
    axios.get("https://v2.jokeapi.dev/joke/Any").then((res) => {
      console.log(res.data);
      //the returned joke is twopart ie. with a setup and a delivery
      if (res.data.type === "twopart") {
        jokeStateSetter(res.data.setup);
        //If speech synthesis compatible, narrate
        if ("speechSynthesis" in window) {
          say(res.data.setup).then(() => {
            setTimeout(() => {
              jokeStateSetter(res.data.delivery);
              if ("speechSynthesis" in window) {
                say(res.data.delivery).then(() => {
                  setTimeout(() => {
                    fetchAndNarrate();
                  }, 4000);
                });
              }
            }, 2000);
          });
          //If speech synthesis not compatible with browser, just display the joke without narration
        } else {
          setTimeout(() => {
            jokeStateSetter(res.data.delivery);
          }, 4000);
          fetchAndNarrate();
        }
        //the returned joke is in a single sentence
      } else if (res.data.type === "single") {
        jokeStateSetter(res.data.joke);
        //If speech synthesis compatible
        if ("speechSynthesis" in window) {
          say(res.data.joke).then(() => {
            setTimeout(() => {
              fetchAndNarrate();
            }, 4000);
          });
        }
        //If speech synthesis not compatible with browser, just display the joke without narration
        else {
          setTimeout(() => {
            jokeStateSetter(res.data.delivery);
          }, 4000);
          fetchAndNarrate();
        }
      }
      // //the returned joke is of neither type
      // else {
      //   fetchAndNarrate();
      // }
    });
  } else 
      return
  };

  const say = function (text) {
    return new Promise((resolve) => {
      const utterance = new SpeechSynthesisUtterance(text);
      utterance.onend = resolve;
      window.speechSynthesis.speak(utterance);
    });
  };

  return (
    <div>
      <h4>{jokeState}</h4>
      <button
        onClick={() => {
          RUNNING = true
          fetchAndNarrate();
        }}>
        Start
      </button>
      <button
        onClick={() => {
          RUNNING = false
          //window.speechSynthesis.cancel();
        }}>
        Stop
      </button>
    </div>
  );
}

当按下Start按钮时,RUNNING被设置为true并执行递归函数fetchAndNarrate ()。在里面,我使用setTimeout 在笑话的设置和交付以及下一个笑话之间稍作停顿。拥有这个递归函数背后的想法是让笑话继续下去,直到我按下stop 按钮。当它被按下时,我将RUNNING 设置为 false。 say() 是一个函数,它返回一个承诺,当笑话被完全叙述时解决。

预期行为:在停止时,当前笑话完成其叙述,该函数应使if(RUNNING) 条件失败并在下一次迭代中返回,而不从 API 获取另一个笑话。

观察到的行为:在停止时,当前笑话完成了旁白,尽管RUNNING 被设置为false,该函数以某种方式继续运行并在下一次迭代中获取另一个笑话并且函数永远不会停止。

我认为我在该函数中有太多嵌套的 Promise,这是我可能搞砸的地方。我是 JavaScript 和 Promises 的新手,我不确定有这么多嵌套的 Promise 是否理想。

请帮助一位兄弟。

【问题讨论】:

    标签: javascript reactjs recursion async-await es6-promise


    【解决方案1】:

    您可以使用 async/await 和几个辅助方法使您的代码更具可读性。当然,我使用了fetch,但同样适用于 axios。没有必要让它递归,一个简单的while 循环就可以了。

    async function run() {    
        while(running) {
            var res = await fetch("https://v2.jokeapi.dev/joke/Any")
            var json = await res.json();
            if(json.type == "twopart") {
                await say(json.setup);
                await delay(1000);
                await say(json.delivery);
            }
            else if(json.type == "single") {
              await say(json.joke)
            }
        }
    }
    

    活生生的例子:

    const say = function(text) {
      out.innerHTML = text
      return new Promise((resolve) => {
        const utterance = new SpeechSynthesisUtterance(text);
        utterance.onend = resolve;
        window.speechSynthesis.speak(utterance);
      });
    };
    const delay = t => new Promise(resolve => setTimeout(resolve, t))
    
    const out = document.querySelector("#output");
    const stop = document.querySelector("#stop")
    stop.addEventListener("click", () => running = false)
    let running = true
    
    async function run() {
    
      while (running) {
        var res = await fetch("https://v2.jokeapi.dev/joke/Any")
        var json = await res.json();
        //console.log(json)
        if (json.type == "twopart") {
          await say(json.setup);
          await delay(1000);
          await say(json.delivery);
        } else if (json.type == "single") {
          await say(json.joke)
        }
      }
    }
    
    run()
    <div id="output">
    
    </div>
    <button id="stop">
    Stop
    </button>

    【讨论】:

      【解决方案2】:

      您将 RUNNING 定义为函数中的局部变量。将其真正定义为全局,例如 window.RUNNING,然后是您的作品。

      【讨论】:

        猜你喜欢
        • 2013-04-08
        • 1970-01-01
        • 2016-03-14
        • 2022-10-13
        • 1970-01-01
        • 2020-04-15
        • 1970-01-01
        • 2012-05-04
        • 1970-01-01
        相关资源
        最近更新 更多