Try again when it fails

Call an unreliable server, and try again a limited number of times.

What to build

An async function retry(limit), which is the pattern real apps use for flaky networks.

Requirements

  • The first input line is the limit: the maximum number of attempts.
  • The second line lists what the stand-in server does on each attempt, ok or fail. The starter reads it and provides callServer() for you.
  • For each failed attempt, print attempt N: failed.
  • For a successful attempt, print attempt N: ok, then result: VALUE with the value the server returned, and stop.
  • If every attempt fails, print gave up after L attempts.

Input

Two lines, as described.

Output

As described above.

Constraints

The limit is from 1 to 10. The list always has at least as many entries as the attempts that will be made.

Example

Input:

3
fail ok

Output:

attempt 1: failed
attempt 2: ok
result: data

Why it works

await turns a rejected promise into a thrown error, so an ordinary try and catch can sit inside an ordinary loop. Returning on success ends both the loop and the function at once.

Examples

Example 1 · Input
3
fail ok
Expected output
attempt 1: failed
attempt 2: ok
result: data
Example 2 · Input
2
fail fail ok
Expected output
attempt 1: failed
attempt 2: failed
gave up after 2 attempts

Hints

Hint 1

Loop from attempt 1 up to the limit, and try callServer inside the loop.

Hint 2

A successful await can return from the function straight away.

Hint 3

If the loop ends, every attempt failed, so report that you gave up.

Solution

Show one correct solution (try it yourself first)
const limit = Number(prompt());
const outcomes = prompt().split(" ");

// A stand-in for a real server: it succeeds or fails as the input says.
function callServer() {
  return new Promise((resolve, reject) => {
    const outcome = outcomes.shift();
    setTimeout(() => (outcome === "ok" ? resolve("data") : reject(new Error("server error"))), 10);
  });
}

async function retry(limit) {
  for (let attempt = 1; attempt <= limit; attempt++) {
    try {
      const value = await callServer();
      console.log(`attempt ${attempt}: ok`);
      console.log(`result: ${value}`);
      return;
    } catch (err) {
      console.log(`attempt ${attempt}: failed`);
    }
  }
  console.log(`gave up after ${limit} attempts`);
}

retry(limit);

Lesson: Asynchronous JavaScript: event loop, promises, async/await →