1

I have an array of strings let symbols = ['abc', 'cde', 'edf', 'qqe', 'hrt'] that I pass as an argument to the function:

async function fetchDetails(symbols) {
  let promises = symbols.map((s, i) => {
    return fetch(URL + s)
      .then(res => return res.json())
      .then(json => Object.assign({}, { [s]: json }))
  });
  console.log('promise:', promises)
  return Promise.all(promises);
}

I fetch data from URL/'abc', URL/'cde' etc. and save it into promises array.

But there is an % probability that from the server I will not get all 5 resolved objects. Then in console.log the promises array looks like this:

enter image description here

And I would like to have array containg only 4 resolved items (that I pass to the Promise.all(), instead of 5 (including 4 resolved and 1 with pending status).

If it was a synchronous function, I would simply have to have filtered the array. But I have no access to the [[PromiseStatus]] properties and have no idea how to do this.

Since I am quite new to the Javascript I would appreciate any help with this Async thing. Any scrap or code, or advise where to search for the answer :)

Edit: Maybe this will help a bit, the route I send a query to, is built like this:

app.get('/data/:symbol', async (req, res) => {
  const { params: { symbol } } = req
  const data = await stocks.getData(symbol, new Date())
  res.send(data)
})

So in the case of error, it doesn't send any error right? And that's why I could potentially have Pending Status instead of Reject?

SOLUTION OF THE TASK

Hey guys, so I solved this issue with 2 things: 1. Thanks to @Bergi, who pointed to the fact that Pending Status is not something that can be omitted - I checked server side and there was a first problem - Errors were not handled. 2. Then after fixing Server side, since I could separate Resolved Promises from Rejected - I was abble to return Array containing only Resolved promises - using this custom Promise_all solution: https://stackoverflow.com/a/46024590

So my final code looks something like this:

async function fetchDetails(symbols) {
  let promises = symbols.map(async (s, i) => {
    return await fetch(URL + s)
      .then((res)=> {
        if (!res.ok) {
          throw new Error('Error with fetch')
        } else {
          return res.json();
        }
      })
      .then(json => Object.assign({}, { [s]: json })) 
      .catch(err => {
        return Promise.reject()})    
  });

  const Promise_all = promises => {
    return new Promise((resolve, reject) => {
      const results = [];
      let count = 0;
      promises.forEach((promise, idx) => {
        promise
          .catch(err => {
            return err;
          })
          .then(valueOrError => {
            results[idx] = valueOrError;
            count += 1;
            if (count === promises.length) resolve(results);
          });
      });
    });
  }; 

  const results = await Promise_all(promises)
  const validResults = results.filter(result => result !== undefined);

  return validResults;
}

Thank you very much to everyone who was writing here!

  • Why does the promise keep pending? How will you know when to stop waiting? – Bergi Dec 08 '17 at 12:55
  • Not sure if it will answer your question, but server simulates errors and there is a 5% chance that it will return an error instead of the data object - in this case, it is pending status (I guess). And when I have such situation I would like to bypass this promise and only store 4 resolved promises in the array. – Michael Lester Dec 08 '17 at 13:06
  • 1
    Well no, if it returns an error status you should reject your promise to *signal* that error instead of leaving it pending. One needs to get notified of the error, otherwise we cannot distinguish the server having returned an error from the server not having returned anything yet. – Bergi Dec 08 '17 at 13:11
  • how long would you like the timeout to be before failing the promise? – HMR Dec 08 '17 at 14:46
  • I Edited my post with some I guess useful information. – Michael Lester Dec 08 '17 at 20:34

1 Answers1

1

If you want to fail after a certain timeout you could do this:

const failIn = milliseconds =>
  setTimeout(
    _=>Promise.reject("timed out")
    ,milliseconds
  );

const Fail = function(details){this.details=details;};

const fetchDetails = symbols =>
  Promise.all(
    symbols.map((s, i) => {
      return Promise.race([
        fetch(URL + s),
        ,failIn(2000)//fail in 2 seconds
      ])
      .then(
        response=>[response,s],
        err = [new fail([err,URL+s]),s]
      )
    })
  )
  .then(
    responses =>
      responses.map(//map to json object from response or Fail type
        ([response,s]) => 
          (response && response.constructor === Fail)
            ? response
            : Object.assign({}, { [s]: response.json() })
      )
  );

The Fail objects are still included in the result, you could use filter to take them out if you're just going to ignore them:

  .then(
    responses =>
      responses.filter(
        ([response,s]) => (response && response.constructor !== Fail)
      )
      .map(
        ([response,s]) => 
          Object.assign({}, { [s]: response.json() })
      )
  )
HMR
  • 37,593
  • 24
  • 91
  • 160
  • 2
    It might be easier to include a boolean in that result tuple than to wrap some values in that `Fail` thingy and others not. – Bergi Dec 08 '17 at 15:08
  • 1
    @Bergi Yes, if you're ignoring rejected promises then you could do `err =[false,s]`, noticed a bug in the reject handler as that one should return a tuple (fixing) – HMR Dec 08 '17 at 16:29