Code inside while loop not being executed JavaScript

一个人想着一个人 提交于 2021-01-28 06:48:04

问题


I am working with this while loop and it is not working. I decided to use the Google Chrome debugger and I saw that the code inside is not being executed.

All the time it checks the condition, starts the first line of the code inside, and goes back again to check the condition.

It is a NodeJS server and I am using the 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('/');
});

回答1:


This question is best understood by understanding how node.js uses an event loop. At its core, node.js runs your Javascript in a single thread and it uses an event loop in order to manage the completion of things outside that single thread such as timers, network operations, file operations, etc...

Let's first start with a very simple while() loop:

let done = false;

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

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

At first blush, you would think that the while loop would run for 100ms and then done would be set to true and the while loop would exit. That is not what happens. In fact, this is an infinite while loop. It runs and runs and runs and the variable done is never set to true. The console.log() at the end never runs.

It has this issue because setTimeout() is an asynchronous operation and it communicates its completion through the event loop. But, as we described above, node.js runs its Javascript as single threaded and only gets the next event from the event loop when that single thread finishes what it's doing. But, the while can't finish what it's doing until done gets set to true, but done can't get set to true until the while loop finishes. It's a stand-off and the while loop just runs forever.

So, in a nutshell, while any sort of loop is running, NO asynchronous operation ever gets its result processed (unless it's using await inside the loop which is something different). Asynchronous operations (or anything that uses the event loop) has to wait until the current running Javascript is done and then the interpreter can go back to the event loop.


Your while loop has the exact same issue. spotifyApi.searchTracks() is an asynchronous operation that returns a promise and all promises communicate their results via the event queue. So, you have the same standoff. Your .then() handler can't get called until the while loop finishes, but your while loop can't finish until the .then() handler gets called. Your while loop will just loop infinitely until you exhaust some system resource and your .then() handlers never get a chance to execute.


Since you haven't included code in your request handler that actually produces some result or action (all it appears to do is just modify some local variables), it's not obvious what exactly you're trying to accomplish and thus how to better write this code.

You appear to have N searches to do and you're logging something in each search. You could do them all in parallel and just use Promise.all() to track when they are all done (no while loop at all). Or, you can sequence them so you run one, get its result, then run another. Your question doesn't give us enough info to know what the best option would be.

Here's one possible solution:

Sequence the operations using async/await

Here the request handler is declared async so we can use await inside the while loop. That will suspend the while loop and allow other events to process while waiting for the promise to resolve.

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:


The reason you're only seeing one line execute is because it's asynchronous. When you call an asynchronous function, it returns immediately and continues to do its work in the background. Once it's done, it calls another function (a "callback") and passes the results of the function to that. That's why your code has to go inside of a call to then() rather than just being on the next line of code.

In this case, spotifyApi.searchTracks() returns a Promise. Once the Spotify API has completed the search, the function in then() will run.




回答3:


Lets use async/await to solve this problem, I have no clue what data you get in a text but I think it is good example to understand a concept of asynchronous processing.

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)
  }
});

For me much simpler and cleaner code, again, i dont know what is structure of your text attribute and logic behind it, so maybe you will have to make some changes.



来源:https://stackoverflow.com/questions/56916549/code-inside-while-loop-not-being-executed-javascript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!