【问题标题】:Conditional .then execution有条件的.then执行
【发布时间】:2019-08-07 19:57:17
【问题描述】:

如何有条件地跳过承诺而不做任何事情。我创建了一个嵌套的 promise,通过它我有 7 个 .then's。但是有条件地,我需要跳过几个 .then 并且在那个块中什么都不做,如何实现这个?

我的完整代码

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 });
 });
 }

从上面的代码中,最后两个 .then 有一个条件 return couponCodeName === "" ?空:代码...)}。

我需要实现的是,当 couponCodeName === "" 那么,它应该跳过 .then 块并且什么都不做。但是,我在此返回 null,它会引发未处理的拒绝错误。那么如何实现呢?如何跳过.then,什么都不做(什么都不做很重要,直接跳过它)怎么做?

我得到的错误是:我从这些嵌套的 .then 中得到的错误是“未处理的拒绝”和“错误:发送后无法设置标头。”

来自 Google Cloud 函数的错误

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)

还有

Unhandled rejection

注意:Node Js 版本:6(所以我认为正式,我不能使用 async 和 await)

【问题讨论】:

  • 您的代码应该可以工作,但是如果 usersCouponUsage 不是空字符串并且其中一个 admin.database 调用拒绝,那么您最终会收到您描述的错误,因为您在你已经完成 res.status(200).send(...) 之后在 catch 中调用 res.status(422)

标签: javascript promise es6-promise


【解决方案1】:

怎么样

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

?

【讨论】:

  • 好的,我返回 couponCodeName === "" ? null:做某事。返回 null 好不好?
  • 为什么?添加解释!
【解决方案2】:

也许您可以为此使用async/await,因为您需要同步:

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

【讨论】:

  • 如果您只有 1 个有条件地调用的异步函数怎么办。 (对不起格式。)async () => { let result = false; let condition = getSystemVariableState(); if (condition) { result = await someAsyncFn(); } return result; }。我知道即使condition 是错误的,async 也会导致函数返回解析为result 的 Promise,这很好,但是是否有任何其他含义或后果使这成为一个坏主意?我进行了很多搜索,但找不到解决此特定问题的任何地方。
  • @WilsonF 有什么问题?一个 async func 总是将结果打包到一个 Promises 中,所以你可以这样做:return condition ? someAsyncFn() : result; 你的例子中不需要同步......
  • 这就是我所怀疑的,但我想确认我没有错过一些我不知道的进一步复杂性。谢谢!
【解决方案3】:

说如果你想忽略这个块:

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

您只需返回一个已解决的承诺即可转到下一个后续,如下所示:

.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 })
        })
}) 

在你的情况下忽略这段代码:

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

这样写:

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

快乐编码?

【讨论】:

  • 这并没有改变什么?
  • @JonasWilms 我们可以提前解决承诺以跳过 then 块,我已经用快照更新了答案。
【解决方案4】:

第 1 部分:您的错误处理程序不应崩溃。

如果您调用res.status(200),Express 会开始将数据流式传输到客户端(标头已发送)。之后您无法使用res.status(500) 更改响应状态,因为状态代码已经在发送给客户端的途中。

 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
 });

要解决此问题,您应该仅在完成所有任务后才开始流式传输任何数据:

 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
   });

第 2 部分:错误中的逻辑根本不应该抛出。

现在错误处理程序可以正常工作,您应该能够找出代码中实际出了什么问题。

(如果没有正确的错误消息,我们无法帮助您)


第 3 部分:实现所需的条件执行:

要有条件地执行 promise,你必须嵌套它们:

 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

第 4 部分:现在一切正常,您可以将代码重构为 async / await 以使其更具可读性:

正如你所指出的,v6 dpes 不支持async / await,你必须要你migrate to v8 或者你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 });
  }
};

【讨论】:

  • 明白了,但是 res.status(200).send({ success:true }) 之后的 .then 就像是有条件的,因为它返回 null 如果 couponCodeName === " " ,返回 null in 。那么可以接受吗?我可以将 res.status(200).send({success:true}) 用于这种有条件的 .then 吗?
  • @Dazzile 我会说返回 null 是一种糟糕的风格,因为它没有任何用处。
  • 是的,我应该做什么而不是使用 null ?而且,感谢您的编辑,因为我的节点版本是 6,所以不支持异步/等待功能这样想(Ps:我在我的问题上添加了这个),(我正在使用谷歌云功能,因为我所有的功能都是基于在节点 6 上)
  • @dazzile 你应该migrate to v8 或者你transpile it down with webpack
  • 你肯定会这样做,因为我在节点 6 上运行了几十个云功能,是否有任何临时解决方案可以运行上述问题,以便我可以慢慢迁移?
猜你喜欢
  • 2018-10-05
  • 2018-07-25
  • 1970-01-01
  • 2020-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-22
  • 2015-01-20
相关资源
最近更新 更多