Search code examples
javascriptes6-promise

JavaScript Promise: Error message comes after the resolved ones


I'm implementing a Generator function that uses a for loop to go through given range of numbers and find the prime ones.

function * findPrimeNumbers (from, to) {
    for (let i = from; i <= to; i++) {
        let isCurrentNumber = true
        for (let j = 2; j < i; j++) {
            if (i % j === 0) {
                isCurrentNumber = false
                break
            }
        }
        if (isCurrentNumber) {
            yield i
        }
    }
}

A Promise is called for each prime number and retrieves its name (e.g. when given 3 will return 'three').

My question is why when the number 2 is given and the error is thrown, the message is logged after the resolved ones. My goal is to log it when the number is question is requested.

const data = [
    {
        id: 5,
        name: 'five'
    },
    {
        id: 7,
        name: 'seven'
    },
    {
      id: 3,
      name: 'three'
    },
    // Two is a prime number and it will not be found.
    // The error message will come after the resolved ones although it is the first number to be requested.
    // Why?
    // {
    //     id: 2,
    //     name: 'two'
    // }
]

function getNumberName (primeNumber, numbers) {
    return new Promise((resolve, reject) => {
        const retrievedNumber = numbers.find(number => number.id === primeNumber)

        if (retrievedNumber) {
            resolve(retrievedNumber.name)
        } else {
            reject(new Error(`The requested number ${primeNumber} is not found!`))
        }
    })
}

const result = findPrimeNumbers(2, 10) // start executing the generator

let isDone = false

while (!isDone) {
    let current = result.next()

    isDone = current.done

    if (!isDone) {
        let numberName = getNumberName(current.value, data)

        numberName
            .then(name => console.log(name))
            .catch(error => console.log(error.message))
    }
}

The result is

three
five
seven
The requested number 2 is not found!

Thank you in advance!


Solution

  • You are creating all the promise at once, so the resolution order is not determined. In order to enforce it you'll need to chain them with something like that:

    const result = findPrimeNumbers(2, 10); // start executing the generator
    
    checkNextNumber(result)
    
    function checkNextNumber(result) {
      let current = result.next();
    
      if(current.done) return;
    
      let numberName = getNumberName(current.value, data);
    
      numberName
        .then(name => console.log(name))
        .catch(error => console.log(error.message))
        .then( () => checkNextNumber(result) )
    }
    

    Which outputs:

    The requested number 2 is not found!
    three
    five
    seven
    

    Edit: Use async

    If you don't like recursion, you can use an async function (which I assume you can use as you are already using generator anyway). As a bonus, I use a for of loop that makes your code more readable:

    async function checkNextNumberAsync(result) {
      for(let current of result) {
        try {
          let name = await getNumberName(current, data);
          console.log(name);
        } catch(e) {
          console.log(e.message)
        }
      }
    }