while 循环中的代码未被执行 JavaScript

Code inside while loop not being executed JavaScript

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

它一直在检查条件,从里面的第一行代码开始,然后再次返回检查条件。

这是一个 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('/');
});

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

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

让我们用 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 属性的结构和它背后的逻辑,所以也许你必须做一些改变。

通过理解 node.js 如何使用事件循环可以最好地理解这个问题。在它的核心,node.js 运行s 你的 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 循环会退出.事实并非如此。实际上,这是一个无限循环。它 运行s 和 运行s 和 运行s 并且变量 done 永远不会设置为 true。最后的 console.log() 永远不会 运行s.

它有这个问题是因为 setTimeout() 是一个异步操作,它通过事件循环来传达它的完成。但是,如上所述,node.js 运行 将其 Javascript 作为单线程,并且仅在该单线程完成其正在执行的操作时才从事件循环中获取下一个事件。但是,whiledone 设置为 true 之前无法完成它正在做的事情,但是 donewhile 之前无法设置为 true循环结束。这是一个 stand-off 而 while 循环只是 运行 永远。

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


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


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

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

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

使用async/await

对操作进行排序

此处声明了请求处理程序 async,因此我们可以在 while 循环中使用 await。这将暂停 while 循环并允许其他事件在等待 promise 解决时进行处理。

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('/');
});