Tometoyou
Tometoyou

Reputation: 8376

Each then() should return a value or throw when using Promises

I have a few async methods that I need to wait for completion before I return from the request. I'm using Promises, but I keep getting the error:

Each then() should return a value or throw // promise/always-return

Why is this happpening? This is my code:

router.get('/account', function(req, res) {
  var id = req.user.uid
  var myProfile = {}
  var profilePromise = new Promise(function(resolve, reject) {
    var userRef = firebase.db.collection('users').doc(id)
    userRef.get()
      .then(doc => { // Error occurs on this line
        if (doc.exists) {
          var profile = doc.data()
          profile.id = doc.id
          myProfile = profile
          resolve()
        } else {
          reject(Error("Profile doesn't exist"))
        }
      })
      .catch(error => {
        reject(error)
      })
  })
  // More promises further on, which I wait for
})

Upvotes: 33

Views: 35138

Answers (5)

Josh
Josh

Reputation: 1267

why is this happening ? Each then() should return a value or throw So I am adding this since the why was never explained.

For anyone else wondering the why. this is not a normal JS Error. This is a direct result of using the ES-Lint promise package:

This package has a ruleset to error when it does not find a return from a promise. This can be helpful to identify promise flow errors. You could fix this by simply adding a return as thats what the linter is triggered by or editing the es-lint rule set (not recommended). Thats why I assume it works when it is not being linted causing your confusion.

 router.get('/account', function(req, res) {
  var id = req.user.uid
  var myProfile = {}
  var profilePromise = new Promise(function(resolve, reject) {
    var userRef = firebase.db.collection('users').doc(id)
    userRef.get()
      .then(doc => { // Error occurs on this line
        if (doc.exists) {
          var profile = doc.data()
          profile.id = doc.id
          myProfile = profile
          return null
        } else {
          reject(Error("Profile doesn't exist"))
        }
      })
      .catch(error => {
        reject(error)
      })
  })
  // More promises further on, which I wait for
})

Here is a list of the default rulesets used by that package. Hope it helps anyone else trying to get background on why this return was needed.

{
  "rules": {
    "promise/always-return": "error",
    "promise/no-return-wrap": "error",
    "promise/param-names": "error",
    "promise/catch-or-return": "error",
    "promise/no-native": "off",
    "promise/no-nesting": "warn",
    "promise/no-promise-in-callback": "warn",
    "promise/no-callback-in-promise": "warn",
    "promise/avoid-new": "warn",
    "promise/no-new-statics": "error",
    "promise/no-return-in-finally": "warn",
    "promise/valid-params": "warn"
  }

Upvotes: 0

Abhin Krishna KA
Abhin Krishna KA

Reputation: 855

If you can't fix this issue but still want to run your code...

open   : eslintrc.json file (search from project root directory)
search : 'promise/always-return'
change : Case 1: if (existing value is 2) => change to 1
         Case 2: else if(existing value is "error" => change to "warn")

It will make this error into warning, but be careful with it... Also use eslint plungin in your editor to remind of good practice. Otherwise you won't get any promise/always-return related warnings.

Also make sure you find the right eslintrc.json if more than 1 appears on your search

Upvotes: 0

Marlhex
Marlhex

Reputation: 1979

Add at the end of the then()

return null

That's it.

Each then() should return a value or throw Firebase cloud functions

Upvotes: 38

Santosh Shinde
Santosh Shinde

Reputation: 6053

In your case firebase.db.collection('users').doc(id) returning promise itself, please check firebase snippet to here for node-js.

If you have multiple promises and you need to call them one by one then use Promises chaining.

Please check this article this will help you.

Use following code in your case,

          router.get('/account', function(req, res) {

            var id = req.user.uid;
            var myProfile = {};

            var userRef = firebase.db.collection('users').doc(id)

            userRef.get()
            .then(doc =>  {

                if (!doc || !doc.exists) {
                   throw new Error("Profile doesn't exist")
                }

                var profile = doc.data();
                profile.id = doc.id;
                myProfile = profile;

               return myProfile;

            })
            .catch(error => {
              console.log('error', error);
            })

          })

And use Promise.all if you have multiple promises and you want's to execute them in once.

The Promise.all(iterable) method returns a single Promise that resolves when all of the promises in the iterable argument have resolved or when the iterable argument contains no promises. It rejects with the reason of the first promise that rejects.

For example:

  var promise1 =  new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'foo1');
  });
  var promise2 =  new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'foo2');
  });
  var promise3 =  new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'foo3');
  });

  Promise.all([promise1, promise2, promise3])
  .then(result =>  console.log(result))
  //result [foo1, foo2, foo3] 

Hopes this will help you !!

Upvotes: 4

Bergi
Bergi

Reputation: 664559

Just avoid the Promise constructor antipattern! If you don't call resolve but return a value, you will have something to return. The then method should be used for chaining, not just subscribing:

outer.get('/account', function(req, res) {
  var id = req.user.uid
  var userRef = firebase.db.collection('users').doc(id)
  var profilePromise = userRef.get().then(doc => {
    if (doc.exists) {
      var profile = doc.data()
      profile.id = doc.id
      return profile // I assume you don't want to return undefined
//    ^^^^^^
    } else {
      throw new Error("Profile doesn't exist")
//    ^^^^^
    }
  })
  // More promises further on, which I wait for:
  // profilePromise.then(myProfile => { … });
})

Upvotes: 29

Related Questions