while循环如何与Nodejs中的承诺和回调函数一起工作?



这是我第一次用Promise和callback编写while循环。我不知道为什么会导致无限循环。如何解决这个问题?

async function getResult(){
return new Promise((resolve, reject) => {
let params ="some input and setting";
let next = "hasNext";
let array = [];
let error = null;
while(next !== null){
checkNext(params, function(err,data) { //checkNext is a function to return the current list and check wether there has the next list
if(err){
next = null;
error = err;
}else{
next = data.hasNext; // if there is not next list, data.hasNext = null
array = array.concat(data.array); // data.array return the current list
}
});
}
if(error !== null){
reject(error);
}else{
resolve(array); // I want to return all lists
}

});
}

它会导致无限循环,因为checkNext()是异步的和非阻塞的,所以你的while()只是永远运行,甚至在一个调用checkNext()之前有机会完成并调用它的回调。

你永远不会使用while()循环等待一些异步的事情在javascript中完成(除了await如下所示),因为nodejs的事件驱动架构,while循环永远不会返回控制回事件循环,所以没有异步操作永远不能得到它的完成事件处理,因此你等待的事情永远不会有机会发生。如果使用await来等待连接到异步事件的承诺,情况就不同了(如下所示)。然后,您可以成功地使用while()循环。

在异步操作中,你要使用承诺,你总是希望承诺你的异步操作,所以你所有的控制流都是承诺,而不是普通的回调,因为两者不能很好地混合。这是我的建议:

const { promisify } = require('util');
const checkNextP = promisify(checkNext);
async function getResult() {
let params = "some input and setting";
let next = "hasNext";
let array = [];
while (next !== null) {
let data = await checkNextP(params);
next = data.hasNext;               // if there is not next list, data.hasNext = null
array = array.concat(data.array);  // data.array return the current list
}
return array;
}

这里,while循环工作是因为我们使用await和从checkNextP()返回的承诺,并且await挂起函数的执行,直到该承诺解决/拒绝。

关于异步函数如何工作的更多解释

当我们到达第一个await时,这个async函数将自动返回一个promise。调用者会在那个时候得到那个承诺。然后,当具有第一个await的promise解决时,该函数将恢复,您将获得第一个data值并执行循环的其余部分。此过程将重复,直到nextnull。此时,while()循环将完成,return array语句将执行。因为这是一个async函数,所以返回语句真正做的是解析async函数之前返回的承诺,并将array设置为该承诺的解析值。

如果,来自checkNextP()的承诺拒绝,那么await checkNextP()将抛出拒绝,因为我们没有try/catch围绕它,async函数将自动捕获该抛出,它将拒绝async函数先前返回的承诺,导致调用者获得承诺拒绝与checkNextP()拒绝的任何错误。因此,这里的错误处理也可以正常工作。

getResult()的调用者,只需要做这样的事情:

getResult().then(results => {
console.log(results);
}).catch(err => {
console.log(err);
});

或者,调用者也可以在async函数中,并使用awaittry/catch来捕获错误。