2

I have an API call that returns either a promise or a object with information.

getFollowers(uid, auth){
    if(uid){
      var followersPromise =  this.db.collectionGroup(`following`)
      ....
      })
        .catch(function(error) {
          ...
      });
      return followersPromise
    }else{
      return {success: 0, error: "Item missing"}
    }
  }

In the case where a uid isn't supplied, I return a simple error message. However, if there is, it returns the promise. To make them consistent, I would like to return the object as a promise, even though it's not waiting on anything.

If this is a decent approach, how can I achieve it?

Thingamajig
  • 4,107
  • 7
  • 33
  • 61
  • You can wrap the response from your api in a promise. I'd block the request from the front-end if uid isn't provided. – JohnSnow Jun 07 '19 at 17:10

2 Answers2

1

There are two approaches:

  1. If the code initiating the operation can't even start it, it's reasonable to throw an error. That would be synchronous.

  2. In situations where the asynchronous process is already underway and you're doing this, or if you simply prefer that the function never throws and always returns a promise, use Promise.reject:

    return Promise.reject(new Error("Item missing"));
    

(You don't have to use an Error with Promise.reject, just like you dont' have to with throw, but it's best practice in both cases.)


Two other comments on getFollowers:

  1. In general, if you already have a promise to work with, e.g. from this.db.collectionGroup in this case, don't use new Promise. Just chain from that promise. See this question's answers for details.

  2. I see in your question that getFollowers has a .catch handler. That's usually not appropriate if the function returns a promise. Just allow the error to propagate.

So if you want to do a synchronous error when you can't even initiate the operation:

getFollowers(uid, auth){
    if(!uid){
        throw new Error("Item missing");
    }
    return this.db.collectionGroup(`following`)
        // ....
    });
}

if you prefer to use a rejected promise instead:

getFollowers(uid, auth){
    return !uid
        ? Promise.reject(new Error("Item missing"))
        : this.db.collectionGroup(`following`)
            // ....
        });
}
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    This is awesome! Thanks x100000000! – Thingamajig Jun 07 '19 at 17:31
  • Question, if I'm going the route of returning the rejected promise if uid doesn't exist, and then it continues into the promise (return this.db.collectionGroup(`following`)), then what happens in the event of not having the catch, but there also being an error that would normally have been caught by it? – Thingamajig Jun 07 '19 at 17:42
  • @DanFein - It gets propagated to whatever's calling `getFollowers`, which in turn either passes it on to whatever called *it* or (if it's at the top level, e.g. an event handler or similar) reports the error. – T.J. Crowder Jun 08 '19 at 07:20
0

Thats what Promise.resolve is for:

 return Promise.resolve({success: 0, error: "Item missing"});

Or you could just make the function async:

 async getFollowers(uid, auth){
   if(uid) {
     try {
      const result =  await this.db.collectionGroup(`following`);
      return result;
     } catch(error) {
       //  ...
     }
  } else {
    return {success: 0, error: "Item missing"}
  }
}
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • Signalling errors with a fulfilled promise is not a good idea. – T.J. Crowder Jun 07 '19 at 17:14
  • @t.j.crowder it depends. this could be a public error returned by an API. – Jonas Wilms Jun 07 '19 at 17:15
  • Then it should be a rejected promise (or a thrown error), not a fulfilled promise. This is the reason we have fulfilled vs. rejected. – T.J. Crowder Jun 07 '19 at 17:18
  • I think thats hard to tell from the code shown. Both versions have usecases. – Jonas Wilms Jun 07 '19 at 17:20
  • Fulfilling a promise on error is a very, very, very rare use case. The most common one I can think of (`fetch` fulfilling if the network op worked, even if the HTTP op did not) introduced a **major** footgun and is a clear example of failed API design. I wouldn't suggest replicating it elsewhere. – T.J. Crowder Jun 07 '19 at 17:25