0

I want to write in Javascript the following pseudo-code:

function asyncOperation() {
   return new Promise((resolve, reject) => {
      ....
   }
}

while (true) { 
    result = asyncOperation()
    if result is good then break the loop;
    else loop again
}

I'll explain it in words: asyncOperation uses a Promise, for example an AJAX call. I want that if the promise is rejected, then asyncOperation will be invoked again. Do you know how it can be done in Javascript?

CrazySynthax
  • 13,662
  • 34
  • 99
  • 183
  • Are you on a platform which supports async/await? I feel like this would be extremely simple using a while with a flag, and a try/catch which sets this flag accordingly. – cjones26 Sep 23 '20 at 16:46
  • async/await is a syntactic sugar of Promises. – CrazySynthax Sep 23 '20 at 16:47
  • Yes, but not all platforms support it. – cjones26 Sep 23 '20 at 16:54
  • Does this answer your question? [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – FZs Sep 23 '20 at 16:57
  • The answer is simple: You have **ABSOLUTELY NO** ways to do that without making your calling code asynchronous. – FZs Sep 23 '20 at 16:59
  • Are you just asking for a promise retry? There are lots of answers with a generic "retry" function that retries a function until the promise returned from it is fulfilled - and there are librareis that do fancy things like exponential backoff for retries and delays – Benjamin Gruenbaum Sep 23 '20 at 17:01
  • @BenjaminGruenbaum, can you name some of these libraries? – CrazySynthax Sep 23 '20 at 17:04
  • https://github.com/sindresorhus/p-retry – Benjamin Gruenbaum Sep 23 '20 at 19:11

3 Answers3

4

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.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • It's definitely a valid answer, but as you know recursion can be very not efficient. Is there any option to do it without a recursion? – CrazySynthax Sep 23 '20 at 16:57
  • 1
    @CrazySynthax - It isn't really recursion because of the asynchronous nature here. There is no stack build-up as the stack fully unwinds before the `.then()` handler is called. And, why don't you just use the `await` option which is simpler. – jfriend00 Sep 23 '20 at 16:57
0

Wouldn't something like this just be sufficient:

async function loopUntilTrue() {
  let success = false;

  while (!success) {
    try {
      await asyncOperation();
      success = true;
    } catch {
      success = false;
    }
  }
}

cjones26
  • 3,459
  • 1
  • 34
  • 51
-1

I created a loop function to do that

let count = 0;
function asyncOperation() {
  return new Promise((resolve, reject) => {
    if (count < 10) {
      console.log('reject', count++);
      reject();
      return;
    }
    resolve(console.log('resolve', count));
  });
}

async function loop() {
  while (true) {
    try {
      await asyncOperation();

      break;
    } catch {
      continue;
    }
  }
}

loop();

The result is this:

reject 0
reject 1
reject 2
reject 3
reject 4
reject 5
reject 6
reject 7
reject 8
reject 9
resolve 10
Hossein Mohammadi
  • 1,388
  • 11
  • 16