0

I am using Mongoose and am in a situation in which I am looking up a customer from the database. If the customer exists, I will return a customerId. However, if the customer does not exist, I want to create them and then return the customer id. In either case, I want the promise to end with a customerID. Anyway, while I have seen in the documentation that is possible to instantly wrap a return value using "Promise.resolve", I am receiving an error. This feature is apparently useful when trying to make synchronous tasks more congruent with Node by wrapping their return values. Anyway, what is a better way to deal with this or return a promise? At the end of the day I want the code to return the customer object or fall through to a catch/error.

TypeError: Promise.resolve is not a constructor

Below is the code (I edited out some security things, but I think I didn't make any serious alterations).

Customer.findOne({email: email}).then((customer) => {
    if (customer) {
        return  Promise.resolve(customer);
    }
    else {
        return new Customer({email: email}).save();
    }    
}).then( function(customer) {...

This is really about Promises, not Mongoose or MongoDb. If you are in a situation in which you are inside of a promise and want to return a value to the chain, what do you do?

David J
  • 1,018
  • 1
  • 9
  • 14
  • For the mongoose API, `return Customer.findOneAndUpdate({ email },{ },{ "upsert": true, "new": true })`. Basically the same as the standard API except `"new": true` rather than `"returnOriginal": false`. No need to chain promises when the database does this already. – Neil Lunn May 04 '18 at 00:12
  • That's beautiful! – David J May 04 '18 at 11:02

1 Answers1

0

You don't need Promise.resolve() here. If a then handler returns a non-promise, the promise returned by the then call will resolve with that value. In other words, it will already be wrapped, so wrapping it again is redundant.

On an unrelated note, the else is also redundant, as return statements exit the currently executing function:

Customer.findOne({email: email}).then((customer) => {
    if (customer) {
        return customer;
    }
    return new Customer({email: email}).save();    
}).then( function(customer) {...

Promise.resolve() and Promise.reject() are generally intended for operations that have special cases where no actual asynchronous operation takes place, but you'd still like them to return promises in those cases for consistency's sake.

As a trivial example, imagine I have a bunch of objects with uid properties that may or may not have a corresponding username property. I want to make sure all of them have a username property, so I write a function I can call on all of them, which will look up the username from a database if it is missing. For consistency I want this function to always return a promise, even if the username is already there and no lookup is actually necessary. Also, I can add a case where, in the event that the uid is missing, the promise will reject without wasting time trying to look up undefined as a uid.

Could look something like this:

function ensureUsername(user) {
    if ('username' in user) return Promise.resolve(user);
    return lookUpUsernameByUid(user.uid)
        .then((username) => {
            user.username = username;
            return user;
        });
}

Promise.resolve() is also quite useful when dealing with values that may or may not be promises. This is because it simply returns the value if it already is a promise.

Let's say you want to write a function foo that invokes some callback function. The callback function may or may not be asynchronous-- i.e. it may return a promise, but is not required to. How do you accomplish this?

It's simple. Just put the result of the callback in Promise.resolve and chain a then onto it:

function foo(cb) {
    return Promise.resolve(cb())
        .then((callbackResult) => {
            // Do whatever...
        });
}

This will work whether cb returns a promise that resolves with callbackResult or simply returns callbackResult directly.

sripberger
  • 1,682
  • 1
  • 10
  • 21
  • I had tried the "Promise.resolve(cb())" and received an error. I don't know if the APIs are different, but in my Node.js/Express this doesn't seem to be valid. I have seen this method a lot in conventional JS. – David J May 04 '18 at 11:09
  • I wouldn't expect it to be applicable in your code because you're not making use of a callback like that. It's simply a generic example that I included as a demonstration of the purpose of `Promise.resolve`. I often find that understanding what something is for makes it easier to understand what it is *not* for. :) – sripberger May 04 '18 at 13:10