Conditional .then execution

元气小坏坏 提交于 2019-12-06 01:34:02

问题


How to conditionally skip a promise and do nothing. I have created a nested promise, by which i have 7 .then's. But conditionally, i need to skip few .then and do nothing in that block, how to achive this ?

My FULL CODE:

const admin = require('firebase-admin');
const rp = require('request-promise');

module.exports = function(req, res) {

const phone = String(req.body.phone).replace(/[^\d]/g, '');
const amount = parseInt(req.body.amount);
const couponCodeName = (req.body.couponCodeName);
const couponUsage = parseInt(req.body.couponUsage);
const usersCouponUsage = parseInt(req.body.usersCouponUsage);
const finalAddress = (req.body.finalAddress);
const planName = (req.body.planName);
const saveThisAddress = (req.body.saveThisAddress);
const orderNumber = (req.body.orderNumber);
const pay_id = (req.body.pay_id);

const options = {
    method: 'POST',
    uri:`https://..........`,
    body: {
        amount
    },
    json: true
};

return admin.auth().getUser(phone)
.then(userRecord => {

    return rp(options)
})
.then((orderResponse) => {
    return admin.database().ref('trs/'+ phone)
        .push({ pay_id: orderResponse.id })
    })
.then(() => {
    return admin.database().ref('ors/'+ phone)
        .push({ pay_id })
})
.then(() => { 
    return saveThisAddress === true ? 
        admin.database().ref('address/'+phone)
            .push({address: finalAddress}) : null
})
.then(() => {
    return admin.database().ref('deliveryStatus/'+phone+'/'+orderNumber)
        .set({ plan: planName === "" ? "Single Day Plan" : planName, delivered: false}, () => {
            res.status(200).send({ success:true })
        })
}) 
.then(() => {
    return couponCodeName === "" ? null : 
        admin.database().ref(`couponCodes/${couponCodeName}`)
            .update({couponUsage: couponUsage + 1 })
})
.then(() => {
    return usersCouponUsage === "" ? null : 
        admin.database().ref(`couponUsage/${phone}`)
            .update({ [couponCodeName]: usersCouponUsage + 1 })
})
.catch((err) => {
    res.status(422).send({ error: err })
})    
 .catch((err) => {
 res.status(422).send({error: err });
 });
 }

From the above code, last two .then has a condition return couponCodeName === "" ? null : code... )}.

What i need to achieve is, when the couponCodeName === "" then, it should skip the .then block and do nothing. But, i am returning null in this, it throws an unhandled rejection error. So how to achieve this ? How to skip a .then, and do nothing ( Its important to do nothing, simply skip it ) How to do this ?

THE ERROR I AM GETTING IS: The error i am getting from these nested .then is "Unhandled rejection" & "Error: Can't set headers after they are sent."

Error From Google Cloud Function

Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:369:11)
at ServerResponse.header (/var/tmp/worker/node_modules/express/lib/response.js:767:10)
at ServerResponse.send (/var/tmp/worker/node_modules/express/lib/response.js:170:12)
at ServerResponse.json (/var/tmp/worker/node_modules/express/lib/response.js:267:15)
at ServerResponse.send (/var/tmp/worker/node_modules/express/lib/response.js:158:21)
at admin.auth.getUser.then.then.then.then.then.then.then.catch.catch (/user_code/request_payment_details.js:86:28)
at process._tickDomainCallback (internal/process/next_tick.js:135:7)

And also

Unhandled rejection

Note: Node Js version : 6 ( So i think officially , i cant use async and await )


回答1:


Maybe you can use async/await for this, as synchronization is what you need:

async function doSomething() {
    var res1 = await promise1();
    if (res1 === xxx) {
        var res2 = await promise2();
    } else {
        ...
    }
}



回答2:


Part 1: Your error handler should not crash.

If you call res.status(200), Express starts streaming data to the client (the headers are already sent). You can't change the response status afterwards with res.status(500), as the status code is already on its way to the client.

 stuff()
 .then(result => {
   res.status(200).send(result); // server starts sending
 }).then(moreStuff) // error occurs here
 .catch(error => {
   res.status(500).send(error); // fails, as server is already sending
 });

To resolve this, you should only start streaming any data when all of your tasks are done:

 stuff().then(moreStuff).then(evenMoreStuff) // error occurs here
   .then(result => {
      res.status(200).send(result); // doesnt get executed
   }).catch(error => {
      console.error(error);
      res.status(500).send("Whoops, server error\n" + error.message); // works
   });

Part 2: The logic inside your error should not throw at all.

Now that the error handler works correctly you should be able to find out what's actually going wrong in your code.

(We can't help you with that without a proper error message)


Part 3: Implement the wanted conditional execution:

To conditionally execute promises, you have to nest them:

 a().then(() => {
  if(!stuff) return; // exit early, continue at next then

  return b().then(c); // conditionally execute b and c
 }).then(rest); // executes after b and c if stuff is true

Part 4: Now that everything is working, you could refactor your code to async / await to make it way more readable:

As you pointed out, v6 dpes not support async / await, you'd have to you migrate to v8 or you transpile it down with webpack:

module.exports = async function(req, res) {
  try {
     //...
     const userRecord = await admin.auth().getUser(phone);
     const orderResponse = await rp(options)

     await admin.database().ref('trs/'+ phone)
      .push({ pay_id: orderResponse.id });

     await admin.database().ref('ors/'+ phone)
      .push({ pay_id })

     if(saveThisAddress === true) {
       await admin.database().ref('address/'+phone)
        .push({address: finalAddress});
     }

     await admin.database().ref('deliveryStatus/'+phone+'/'+orderNumber)
      .set({ plan: planName === "" ? "Single Day Plan" : planName, delivered: false});

     if(couponCodeName !== "") {       
       await admin.database().ref(`couponCodes/${couponCodeName}`)
        .update({couponUsage: couponUsage + 1 });
     }

     if(usersCouponUsage !== "") {
       await admin.database().ref(`couponUsage/${phone}`)
        .update({ [couponCodeName]: usersCouponUsage + 1 });
     }

     res.status(200).send({ success:true });
  } catch(error) {
    console.error("Error inside API", error);
    res.status(422).send({ error });
  }
};



回答3:


How about

.then(() => {
            return couponCodeName === "" ? null : 
                admin.database().ref(`couponCodes/${couponCodeName}`)
                    .update({couponUsage: couponUsage + 1 })
                .then(() => {
                            admin.database().ref(`couponUsage/${phone}`)
                                .update({ [couponCodeName]: usersCouponUsage + 1 })
                            })}

?




回答4:


Say if you want ignore this block:

.then(() => {
    return admin.database().ref('deliveryStatus/'+phone+'/'+orderNumber)
        .set({ plan: planName === "" ? "Single Day Plan" : planName, delivered: false}, () => {
            res.status(200).send({ success:true })
        })
}) 

You simply return a resolved promise to go to next following then, like this:

.then(() => {
    if (<some-conditions>) {
       return Promise.resolve(<optional-data>);   
    } else {    // If directly want to go to first catch statement following
       return Promise.reject(<optional-data>)
    }

    return admin.database().ref('deliveryStatus/'+phone+'/'+orderNumber)
        .set({ plan: planName === "" ? "Single Day Plan" : planName, delivered: false}, () => {
            res.status(200).send({ success:true })
        })
}) 

In your case to ignore this block of code:

.then(() => {
    return couponCodeName === "" ? null : 
        admin.database().ref(`couponCodes/${couponCodeName}`)
            .update({couponUsage: couponUsage + 1 })
})

Put it like this:

.then(() => {
    return couponCodeName ? Promise.resolve() :      // "" and null evaluates to false
        admin.database().ref(`couponCodes/${couponCodeName}`)
            .update({couponUsage: couponUsage + 1 })
})

Happy Coding 🎉



来源:https://stackoverflow.com/questions/55204594/conditional-then-execution

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!