0

So, I'm trying to pass values in my ejs file, but it's not working!

Except for the 'shortL' I'm getting [object promise] for 'first', 'second' and 'third' parameters, as for 'des' and 'gen' no values are returned.

I have tried every possible solution but nothing worked. Please help me!

This is my code:

app.get("/dashboard",(req,res,next)=>{
  db.auth().onAuthStateChanged((user) => {
    if (user) {
        
      var uid = user.uid;
      db.firestore().collection("Users").doc(uid).get()
      .then(function (doc){
          if(doc.exists){
              var shortL =doc.data().short_link
              var markId = doc.data().markid;
              
              //rosan's db
             let firstColl =secondarydb.firestore().collection("Affiliate_Marketing").doc(markId);
             let first =firstColl.collection('data').doc('install').get()
             .then(
                 function(doc){
                if(doc.exists){
                    var install =doc.data().number
                    return install;
                    }else{
                        console.log('No such document!');
                    }
                    
                })
            .catch(function(err){
                    console.log(err);
                })
                let secondColl= secondarydb.firestore().collection("Affiliate_Marketing").doc(markId);
               let second= secondColl.collection('data').doc('login').get()
                .then(
                    function (doc){
                    if(doc.exists){
                        var login =doc.data().number
                        return login;
                        }else{
                            console.log('No such document!');
                        }
                })
                .catch(function(err){
                        console.log(err);
                    })
                let thirdColl= secondarydb.firestore().collection("Affiliate_Marketing").doc(markId);
                let third=thirdColl.collection('data').doc('purchases');
                third.get()
                    .then(function (doc){
                        if(doc.exists){
                            var puchases=doc.data().free_trial
                            return puchases;
                            }else{
                                console.log('No such document!');
                            }
                    })
                    .catch(function(err){
                            console.log(err);
                        })
               let pur= third.collection('users').doc("hz71u4cgZA6JQ9IR6Yeo").get()
                    .then(function(doc){
                    if(doc.exists){
                        var des = doc.data().skuDetails.description
                        
                         return des;
                          
                        }else{
                            console.log('No such document!');
                        }

                    })
                .catch(function(err){
                        console.log(err);
                    })
                    res.render("dash",{shortL:shortL,first:first,second:second,third:third,des:pur[0],gen:pur[1]});  
              }else{
                  console.log('No such document!');
              }
              
        })
      .catch(function(err){
              console.log(err);
        })
    } else {
      console.log("User signed out");
    }
    
});
})
Bergi
  • 630,263
  • 148
  • 957
  • 1,375

2 Answers2

0

Keep your head up, like, above the water. You shouldn't be nesting promises then and catch calls so deeply. Additionally, separate these chunks of code into logical functions, such as getUser and getMarketing -

app.get("/dashboard",(req,res,next)=>{
  db.auth().onAuthStateChanged(async user => {
    if (!user) next(Error("Login required"))
    const { short_link, markid } = await getUser(user.uid)
    const { install, login, purchases, sku: [des, gen] } = await getMarketing(markid)
    res.render({short_link, install, login, purchases, des, gen })
  })
})

These independent functions like getUser are much easier to manage. Note, you don't need to attach .catch handlers directly to every promise as they will automatically bubble up once you sequence them properly -

async function getUser(uid) {
  const doc = await db.firestore().collection("Users").doc(uid).get()
  if (doc.exists)
    return doc.data()
  else
    throw Error(`User not found`)
}

getMarketing is definitely more involved, but not necessarily more sophisticated. The biggest difference here is I put the four sub-queries into an array and used Promise.all to fetch all of them simultaneously. Simply check exists for each doc and return the data of your interest. Otherwise throw an error -

async function getMarketing(markid) {
  // refs
  const ref = secondarydb.firestore().collection("Affiliate_Marketing").doc(markid)
  const installRef = ref.collection('data').doc('install')
  const loginRef = ref.collection('data').doc('login')
  const purchasesRef = ref.collection('data').doc('purchases')
  const skuRef = purchasesRef.collection('users').doc("hz71u4cgZA6JQ9IR6Yeo")
  // docs
  const docs = [
    installRef.get(),
    loginRef.get(),
    purchasesRef.get(),
    skuRef.get()
  ]
  // fetch
  const [installDoc, loginDoc, purchasesDoc, skuDoc] = await Promise.all(docs)
  // combine result
  if (installDoc.exists && loginDoc.exists && purchasesDoc.exists && skuDoc.exists)
    return {
      install: installDoc.data().number,
      login: loginDoc.data().number,
      purchases: purchasesDoc.data().free_trial,
      sku: skuDoc.data().skuDetails.description
    }
  else
    throw Error(`Marketing data not found`)
}

Another approach is to break getMarketing down even more -

async function getMarketingInstall(markid) {
  const ref = secondarydb.firestore().collection("Affiliate_Marketing").doc(markid)
  const doc = await ref.collection('data').doc('install').get() // <-
  if (doc.exists)
    return doc.data()
  else
    throw Error("Not found")
}

async function getMarketingLogin(markid) {
  const ref = secondarydb.firestore().collection("Affiliate_Marketing").doc(markid)
  const doc = await ref.collection('data').doc('login').get() // <-
  if (doc.exists)
    return doc.data()
  else
    throw Error("Not found")
}

async function getMarketingPurchases(markid) {
  // STOP
}

See how you repeated yourself once already? And with the third function, you're about to do it again. Abstract the pattern with a function and use a parameter where specialization is required, docid -

async function getMarketingData(markid, docid) {
  const ref = secondarydb.firestore().collection("Affiliate_Marketing").doc(markid)
  const doc = await ref.collection('data').doc(docid).get() // <- second parameter
  if (doc.exists)
    return doc.data()
  else
    throw Error("Not found")
}

Now with a reusable function, each doc takes care of its own fetch, exists check, data retrieval, and potential error. Combining them is now much easier -

async function getMarketing(markid) {
  const docs = [
    getMarketingData(markid, "install"), // <- second argument
    getMarketingData(markid, "login"),   // <- second argument
    getMarketingData(markid, "purchases"), // <- second argument
    getMarketingSku(markid)
    ...
  ]
  return Promise.all(docs)
}

Getting the sku details is unique and so that would be it's own function

async function getMarketingSku(markid) {
  // TODO: reader exercise. give it a shot!
}

You could write the same thing without async and await, but nowadays why would you? The nicest thing about using these control operators is they keep your variables in the same scope. By contrast, then isolates the results in separated scopes -

app.get("/dashboard",(req,res,next)=>{
  db.auth().onAuthStateChanged(user => {
    if (!user) next(Error("Login required"))
    getUser(user.uid)
     .then(userData =>
       getMarketing(userData.markid)
        // combine
        .then(markData => {...userData, ...markData})
     )
     // resume outer chain
     .then(({ short_link, markid, install, login, purchases, sku: [des, gen] }) =>
         res.render({short_link, install, login, purchases, des, gen })
     )
  })
})

Or the alternative where you are forced to deepen with nested then. Neither of these programs are ideal -

app.get("/dashboard",(req,res,next)=>{
  db.auth().onAuthStateChanged(user => {
    if (!user) next(Error("Login required"))
    getUser(user.uid)
     .then(({ short_link, markid }) =>
       getMarketing(markid)
         // down the rabbit hole we go ...
         .then(({ install, login, purchases, sku: [des, gen] }) =>
            res.render({short_link, install, login, purchases, des, gen })
         )
     )
  })         // <- look at this ragged edge of ) } }} ), gross
})
Mulan
  • 129,518
  • 31
  • 228
  • 259
-1

The beautiful thing with then() functions is that they support Promises and Return/catches.

Ideally, you should be returning async methods direction in a then(), and when handling secondary requests, you can make the parent then() an async function and await results from within

.then(async(snapshot)=>{
const res = await myAsyncMethod()
    .then(result => result.map(example=> example.sort()))
    .catch(err => throw err);
return res;
})
.then(result => console.log(result))
.catch(console.log);

It is also HIGHLY important to use arrow functions over anonymous functions, classic function() calls don't have binding, making calls in most frameworks independent, resulting in this.state.etc being null/undefined.

DIGI Byte
  • 4,225
  • 1
  • 12
  • 20
  • Where should I call res.render? – user16347260 Jul 03 '21 at 14:20
  • ideally when all the elements are ready to be pushed to the front of your app, most often at the bottom of the function - https://stackoverflow.com/a/21843991/2301161 – DIGI Byte Jul 03 '21 at 14:22
  • I'm actually new to all this, so what exactly is newPromiseEvent()? – user16347260 Jul 03 '21 at 14:42
  • @user16347260 the majority of code examples are low quality and offer little explanation and unfortunately beginners do not have the requisite skills to know the difference. – Mulan Jul 03 '21 at 15:45
  • This code is incomplete and has many issues. 1) I assume the `then` is supposed to be `.then`, but what is the beginning of the expression? 2) `newPromiseEvent` is not a standard thing, nor is it defined here. 3) `.sort` is typically a function call on array types, but here it is not being used as a function. 4) `catch` and re-`throw` an error doesn't make any sense, nor can you `throw` in an expression, it would need to be in a block, `{ ... }`, 5) `.catch(console.log())` is also broken, as `.catch` expects a function. `.catch(console.log)` would work, but `.catch(console.error)` is better – Mulan Jul 03 '21 at 15:46
  • 6) verbiage around "classic" `function()` calls is inaccurate. `function` declarations and expressions do have lexical scope, the only difference is arrow functions have a lexical _context_, which applies to the `this` variable. It's a strange point to make in this answer because your code doesn't use `this`, so your use of `function` is not a problem. – Mulan Jul 03 '21 at 15:50