If you're going to use a loop, then you will have to use await because otherwise, your loop will just run forever and never give the asynchronous operation any cycles to actually process its completion:
while (true) { 
    result = await asyncOperation()
    if (good result) {
         break;
    }
}
This, of course, needs to be inside a function declared as async.
Before await, the usual way to do this would be with a local function and a recursive-looking structure (that by the way does not have any stack build-up from recursion because of the async operation):
function run() {
    return asyncOperation().then(result => {
        if (good result) {
            return result;
        } else {
            // try again
            return run();
        }
    });
}
run().then(result => {
   console.log(result);
}).catch(err => {
   console.log(err);
});
And, typically you would insert some small delay before calling the asyncOperation() again in order to avoid hammering the target server.  And, you would have either a timeout or a max number of retries to avoid a situation where this loop runs forever.