2

I'm finally trying to get to grips with Promises, one of the newer and more mysterious corners of JavaScript. The following is a problem I came across in a technical test, and which seems to get to the heart of what I don't understand about Promises.

The problem is as follows:

  • You have a predefined class called Service, with the following methods:
    • generate() - which generates a random number between 0 and 100.
    • guess(yourGuess) - which takes a given guess for the random number, and returns a Promise. This Promise, in turn will do one of two things within 100 ms:
      • If the guess is correct, the Promise will be resolved, with the guess corresponding to that Promise as its first argument.
      • If the guess is incorrect, the Promise will be rejected.
    • submit(yourGuess) - which submits a guess you believe is correct.
  • You have to write an asynchronous function, main(), which will:
    • Generate a random number using a Service object.
    • Submit a correct guess to the same object within 400 ms.
    • Any rejected Promises have to be caught.

This is my code:

const MAX_NUMBER = 100;

async function main()
{
    const service = new Service();
    let guessInPromise;

    service.generate();

    for(let i = 0; i <= MAX_NUMBER; i++)
    {
        service.guess(i)
            .then(guessInPromise => service.submit(guessInPromise));
            .catch(err => console.log(err));
    }

    return service;
}

Would my code get the job done? Is there anything I've obviously misunderstood about Promises and asynchronous functions?

Tom Hosker
  • 526
  • 2
  • 17
  • does `generate()` or `guess()` return Promise objects? – Always Learning Mar 23 '21 at 13:46
  • 4
    `const guessInPromise;` makes no sense, as you do not give it an initial value and cannot give it a value later. – crashmstr Mar 23 '21 at 13:49
  • 2
    I think the trick here is to map all possible guesses to promises using `Service.guess(...)` and then wait for [`Promise.any()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any). – Yoshi Mar 23 '21 at 13:53
  • @crashmstr in fact, it should just error - declaring a const without assignment is meaningless and a forbidden syntax. – VLAZ Mar 23 '21 at 13:54
  • 1
    This code would function, yet there are some oddities. Why return `service`? Wouldn't it make sense to log "The correct number was X". Instead of a whole bunch of floating promises, it'd be best to return a result once you have a correct guess. Your code currently immediately returns the service object. Edit: I see Yoshi beat me to it. – samthecodingman Mar 23 '21 at 13:56
  • I would recommend you check out https://stackoverflow.com/q/14220321/1260204 – Igor Mar 23 '21 at 13:59
  • @AlwaysLearning As I mentioned above, `guess()` returns a Promise. `generate()` returns nothing. – Tom Hosker Mar 23 '21 at 14:27
  • @VLAZ Apologies. That should be a `let`. I'll amend my question. – Tom Hosker Mar 23 '21 at 14:29
  • @samthecodingman Yeah, I thought the `return service;` thing was weird too. However, the problem came with some code and then an `// Insert your code here` comment, and the return statement was part of the original code, which you **weren't** supposed to alter. – Tom Hosker Mar 23 '21 at 14:33
  • @Yoshi If you were to flesh that comment out into an answer, and if you could assure me that your code would `submit()` a correct guess within 400 ms, I would definitely accept that answer. – Tom Hosker Mar 23 '21 at 14:35
  • @TomHosker ah, if that's the case then it's also expecting you to be using `async`/`await` syntax in your solution. In regards to `guessInPromise`, it's never set, even inside the callback you are passing to `then()`. Just delete it. – samthecodingman Mar 23 '21 at 14:38

1 Answers1

2

Given your requirements, I'd assume the task is to somehow guess the correct answer in 400ms whereas each guess takes 100ms. A very simple solution here would be to do all guesses in parallel and only wait for the first to be resolved.

This would work because Service.guess is described to only resolve it's returned promise for valid guesses (others will be rejected).

Very helpful here would be: Promise.any

And an example could be:

// very simple mock implementation for the described Service class
class Service
{
  value = null;

  generate() {
    this.value = Math.floor(Math.random() * 101);
  }

  guess(yourGuess) {
    return new Promise(((resolve, reject) => {
      setTimeout(() => {
          yourGuess === this.value ? resolve(yourGuess) : reject()
      }, 100);
    }));
  }

  submit(yourGuess) {
    return yourGuess === this.value;
  }
}

const MAX_NUMBER = 100;

(async function main() {
  const service = new Service();

  service.generate();

  try {
    // await first resolved guess
    const assumedCorrect = await Promise.any(
      // map 0 ... 100 to service.guess(...)
      Array.from({length: MAX_NUMBER + 1}).map((_, i) => service.guess(i))
    );

    console.log(
      assumedCorrect,
      service.submit(assumedCorrect)
    );
  }
  catch {
    console.log('No guess was correct... :(');
  }
})();
Yoshi
  • 54,081
  • 14
  • 89
  • 103
  • Apologies. One thing I forgot to mention is that the question specifies that any rejected Promises - `guess()` will reject if the guess in question is incorrect - have to be **caught**. Would sticking a `.catch()` after the `.any()` do the trick? – Tom Hosker Mar 23 '21 at 14:48
  • Ah ok, give me a moment. Though, given how `Promise.any` works, only if no guess is correct it would be rejected. So which one should be caught? – Yoshi Mar 23 '21 at 14:49
  • I added a simple try/catch block. Though you would need to reduce `MAX_NUMBER` and try a few times, to actually trigger the catch (for the case no valid guess was made). – Yoshi Mar 23 '21 at 14:58