while 循环如何与 Nodejs 中的 promise 和回调函数一起使用?

How the while loop works with promise and callback function in Nodejs?

这是我第一次用Promise和回调编写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() 循环等待 Javasacript 中的一些异步事情完成(除了 await 如下所示)因为在 nodejs 的事件驱动架构中,while 循环从不 returns 将控制权交还给事件循环,因此没有异步操作永远无法处理其完成事件,因此您正在等待的事情永远不会有机会发生。如果您使用 await 等待连接到您的异步事件的承诺,情况会有所不同(如下所示)。然后,你可以成功地使用一个while()循环。

对于要使用 promises 的异步操作,您几乎总是希望对异步操作进行 promisify,以便所有控制流都带有 promises,而不是简单的回调,因为两者不能很好地混合。这就是我的建议:

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 并承诺 return 来自 checkNextP() 并且 await 暂停执行函数直到那个承诺 resolves/rejects.

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

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

如果来自 checkNextP() 的承诺被拒绝,那么 await checkNextP() 将抛出拒绝,并且由于我们没有围绕它的 try/catchasync函数将自动捕获该抛出并且它将拒绝 async 函数先前已 returned 的承诺,导致调用者得到承诺拒绝,无论 checkNextP() 拒绝什么错误。所以,这里的错误处理也是有效的。

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

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

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