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.