0

This is my first time to write while loop with Promise and callback. I don't know why it leads to infinite loop. How to fix it?

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
        }
       
    });
}
LiLian
  • 289
  • 2
  • 14

1 Answers1

2

It leads to an infinite loop because checkNext() is asynchronous and non-blocking so your while() just runs forever before even one call to checkNext() gets a chance to finish and call its callback.

You never use a while() loop waiting for some asynchronous things to finish in Javsacript (except with await as shown below) because with the event driven architecture of nodejs, the while loop never returns control back to the event loop so no asynchronous operation can never get its completion event processed and thus the thing you are waiting for never gets a chance to happen. Things are different (as shown below) if you use await to await a promise that is connected to your asynchronous event. Then, you can use a while() loop successfully.

With asynchronous operations where you're going to use promises, you pretty much always want to promisify your asynchronous operations so all your control flow is with promises, not plain callbacks as the two don't mix very well. This is what I would suggest:

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

Here, the while loop works because we're using await with a promise returned from checkNextP() and that await suspends the execution of the function until that promise resolves/rejects.

A little more explanation about how async functions work

At the point we hit the first await this async function will automatically return a promise. The caller will get that promise at that point. Then, when the promise with that first await resolves, the function will resume, you will get the first data value and the rest of your loop executes. This process will repeat until next is null. At that point, your while() loop will be done and the return array statement will execute. Because this is an async function, what that return statement really does is it resolves the promise that the async function previously returned and sets the array to be the resolved value of that promise.

If, the promise from checkNextP() rejects, then the await checkNextP() will throw the rejection and since we don't have a try/catch around it, the async function will automatically catch that throw and it will reject the promise that the async function has previously returned, causing the caller to get a promise rejection with whatever error checkNextP() rejected with. So, the error handling here works too.

The caller of getResult(), just needs to do something like this:

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

Or, the caller could also be in an async function itself and use await and try/catch to catch errors.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • I wanted to write more or less the same but you did it faster. And better. I'd just want to leave a useful link for reference: [How do I convert an existing callback API to promises?](https://stackoverflow.com/q/22519784) – VLAZ Mar 02 '21 at 00:21
  • Thanks, my friend. A lot of things learn from your post. – LiLian Mar 02 '21 at 01:08
  • @LiLian - Glad that explained things for you. If this answered your question, then you can indicate that to the community by clicking the checkmark to the left of the answer. That will also earn you some reputation points for following the proper procedure here. – jfriend00 Mar 02 '21 at 01:12