【问题标题】:Code inside while loop not being executed JavaScript未执行 while 循环内的代码 JavaScript
【发布时间】:2023-03-29 13:14:01
【问题描述】:

我正在使用这个 while 循环,但它不工作。我决定使用谷歌浏览器调试器,我看到里面的代码没有被执行。

它一直检查条件,开始内部代码的第一行,然后再次返回检查条件。

这是一个 NodeJS 服务器,我正在使用 Spotify API。

app.get('/process', ensureAuthenticated, function (req, res) {
  let init_array = text.split(" ");
  let modtext = init_array;

  while (init_array.length != 0) {
    spotifyApi.searchTracks(modtext.join(" "))
      .then(function (data) {
        try {
          console.log(data.body.tracks.items[0].name);
          for (let i = 0; i < modtext.length; i++) {
            init_array.shift();
          }
          modtext = init_array;
        } catch (err) {
          console.log("No song");
          modtext.pop();
        }
      });
  }


  res.redirect('/');
});

【问题讨论】:

  • let init_array = text.split(" ");中,text的定义在哪里?
  • 在代码的开头。无所不能。
  • 旁注,通过执行modtext.join(" "),您只需重复相同的调用 n* 该数组中有多少项。
  • 我不明白你的意思。但这不是问题,我将那部分更改为“Love”(这是一首歌的标题),同样的情况发生了。

标签: javascript node.js spotify-app


【解决方案1】:

这个问题最好通过理解 node.js 如何使用事件循环来理解。在其核心,node.js 在单线程中运行您的 Javascript,它使用事件循环来管理该单线程之外的事情的完成,例如计时器、网络操作、文件操作等......

让我们先从一个非常简单的while()循环开始:

let done = false;

setTimeout(() => {
    done = true;
}, 100);

while(!done) {
     // do whatever you want here
}
console.log("done with while loop");    // never gets called

乍一看,您会认为while 循环将运行100 毫秒,然后done 将设置为true,while 循环将退出。事实并非如此。事实上,这是一个无限的while循环。它运行,运行,运行,变量done 永远不会设置为true。最后的console.log() 永远不会运行。

它有这个问题,因为setTimeout() 是一个异步操作,它通过事件循环传达其完成。但是,如上所述,node.js 将其 Javascript 作为单线程运行,并且仅在该单线程完成其正在执行的操作时从事件循环中获取下一个事件。但是,直到done 设置为truewhile 才能完成它正在做的事情,但在while 循环完成之前,done 不能设置为 true。这是一个僵局,while 循环永远运行。

因此,简而言之,当任何类型的循环运行时,都不会处理任何异步操作的结果(除非它在循环内使用await,这是不同的)。异步操作(或任何使用事件循环的操作)必须等到当前运行的 Javascript 完成,然后解释器才能返回事件循环。


您的 while 循环有完全相同的问题。 spotifyApi.searchTracks() 是一个异步操作,它返回一个 Promise,所有 Promise 都通过事件队列传达它们的结果。所以,你有同样的僵局。在while 循环完成之前,您的.then() 处理程序无法被调用,但在.then() 处理程序被调用之前,您的while 循环无法完成。您的while 循环将无限循环,直到您耗尽一些系统资源并且您的.then() 处理程序永远没有机会执行。


由于您没有在您的请求处理程序中包含实际产生某些结果或操作的代码(它似乎只是修改一些局部变量),因此您究竟想要完成什么以及如何完成并不明显最好写这段代码。

您似乎有 N 次搜索要做,并且每次搜索都记录了一些内容。您可以并行执行所有操作,只需使用Promise.all() 跟踪它们何时完成(根本没有while 循环)。或者,您可以对它们进行排序,以便运行一个,获取结果,然后运行另一个。您的问题没有为我们提供足够的信息来了解最佳选择。

这是一种可能的解决方案:

使用 async/await 对操作进行排序

这里的请求处理程序被声明为async,所以我们可以在while循环中使用await。这将暂停while 循环并允许其他事件在等待承诺解决时处理。

app.get('/process', ensureAuthenticated, async function (req, res) {
  let init_array = text.split(" ");
  let modtext = init_array;

  while (init_array.length != 0) {
    try {
      let data = await spotifyApi.searchTracks(modtext.join(" "));
      console.log(data.body.tracks.items[0].name);
      for (let i = 0; i < modtext.length; i++) {
        init_array.shift();
      }
      modtext = init_array;
    } catch (err) {
      console.log("No song");
      modtext.pop();
    }
  }
  res.redirect('/');
});

【讨论】:

  • 没错,这就是问题所在。非常感谢您的解释。我可以理解为什么它以前不起作用。
【解决方案2】:

您只看到一行执行的原因是因为它是异步的。当您调用异步函数时,它会立即返回并继续在后台工作。完成后,它会调用另一个函数(“回调”)并将函数的结果传递给该函数。这就是为什么你的代码必须放在对then() 的调用中,而不仅仅是在下一行代码中。

在这种情况下,spotifyApi.searchTracks() 返回一个Promise。一旦 Spotify API完成搜索,then() 中的函数将运行。

【讨论】:

  • 但实际上,我的代码在then() 内没有?调试它我注意到spotifyApi.searchTracks(modtext.join(" ") 行中modtext.join(" ") 的内容在执行期间没有改变。但如果我没有错,它应该会改变。感谢您的帮助。
【解决方案3】:

让我们使用 async/await 来解决这个问题,我不知道你在 text 中得到了什么数据,但我认为这是理解异步处理概念的好例子。

app.get('/process', ensureAuthenticated, async function (req, res, next) {
  try {
    const modtext = text.split(" ");
    const promises = modtext.map(item => spotify.searchTracks(item));
    const response = await Promise.all(promises);

    response.forEach(data => {
       // if you use lodash, simply use a get function `get(data, 'body.tracks.items[0].name')` => no need to check existence of inner attributes
       // or use something like this
       if (data && data.body && data.body.track && data.body.tracks.items && data.body.tracks.items[0]) {
          console.log(data.body.tracks.items[0].name);
       } else { 
          console.log('No song');
       }
    });

    res.redirect('/');
  } catch(err) {
     next(err)
  }
});

对我来说,代码更简单、更清晰,我不知道你的 text 属性的结构和它背后的逻辑,所以也许你必须做一些改变。

【讨论】:

    最近更新 更多