我正在使用这个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('/');
});
答案 0 :(得分:2)
通过了解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,并且仅在该单线程完成其工作时从事件循环中获取下一个事件。但是,while
在done
设置为true
之前无法完成其工作,但是done
在while
之前不能设置为true循环完成。这是一个僵局,而while循环将永远运行。
因此,简而言之,在运行任何类型的循环时,都不会处理异步操作的结果(除非它在循环内使用await
,这是有所不同的)。异步操作(或任何使用事件循环的操作)必须等到当前正在运行的JavaScript完成后,然后解释器才能返回事件循环。
您的while循环有完全相同的问题。 spotifyApi.searchTracks()
是一个异步操作,返回一个诺言,所有诺言都通过事件队列传达其结果。因此,您有相同的僵局。在.then()
循环结束之前,您的while
处理程序无法被调用,但是在while
处理程序之前,您的.then()
循环无法完成。您的while
循环将无限循环,直到您耗尽一些系统资源并且.then()
处理程序永远都没有执行的机会。
由于您没有在请求处理程序中包含实际上会产生某些结果或动作的代码(它似乎要做的只是修改一些局部变量),因此您不清楚要执行的操作以及如何执行最好编写这段代码。
您似乎要进行N次搜索,并且每次搜索都记录了一些内容。您可以并行执行所有操作,而只需使用Promise.all()
来跟踪它们何时完成(根本没有while
循环)。或者,您可以对它们进行排序,以便运行一个,获取其结果,然后运行另一个。您的问题没有给我们足够的信息来知道最好的选择。
这是一种可能的解决方案:
使用异步/等待对操作进行排序
此处将请求处理程序声明为async
,因此我们可以在await
循环内使用while
。这将暂停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('/');
});
答案 1 :(得分:1)
之所以只看到一行执行是因为它是异步的。调用异步函数时,它将立即返回并继续在后台执行其工作。一旦完成,它将调用另一个函数(“回调”)并将该函数的结果传递给该函数。这就是为什么您的代码必须放在对then()
的调用中,而不仅仅是在下一行代码中的原因。
在这种情况下,spotifyApi.searchTracks()
返回一个Promise
。搜索完成 后,then()
中的函数将运行。
答案 2 :(得分:0)
让我们使用异步/等待来解决此问题,我不知道您在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
属性的结构以及其背后的逻辑,因此也许您需要进行一些更改。