【问题标题】:Unhandled rejection Error: Can't set headers after they are sent in JavaScript未处理的拒绝错误:在 JavaScript 中发送标头后无法设置标头
【发布时间】:2019-01-31 22:55:21
【问题描述】:

我无法理解 JavaScript 中的异步性。我认为我的代码应该停在我评论的地方(它转到 if),但它抛出了一个错误:

未处理的拒绝错误:发送后无法设置标头。

很抱歉这么大的代码,但我想把所有的东西都发给你,让你清楚地了解情况。

我认为我的方法 setTransferHistory(...)sendAuthorizationKey(...) 应该是 async/await 并且在执行此代码之后,我想返回状态 200。

Transaction.findOne({
                where: {
                  id_sender: senderId,
                  id_recipient: recipientId,
                  amount_money: amountMoney,
                  transfer_title: transferTitle,
                  authorization_key: authorizationKey,
                  authorization_status: setAuthorizationStatus(0),
                },
                order: [['date_time', 'DESC']],
              }).then(isAuthorizationKey => {
                if (!isAuthorizationKey) {
                  setTransferHistory(
                    senderId,
                    recipientId,
                    amountMoney,
                    transferTitle,
                    authorizationKey,
                  );
                  sendAuthorizationKey(
                    senderId,
                    recipientId,
                    amountMoney,
                    authorizationKey,
                  );
                  return res.status(200).json({ success: true }); /* it should stop in this place */
                }

所有控制器:

exports.register = (req, res) => {
  function getTodayDate() {
    const today = new Date();
    return today;
  }

  function setAuthorizationStatus(status) {
    const authorizationStatus = status;
    return authorizationStatus;
  }

  async function getSenderEmail(id) {
    try {
      const isUser = await User.findOne({
        where: {
          id,
        },
      });
      return isUser.email;
    } catch (e) {
      /* just ignore */
    }
  }

  async function getRecipientName(id) {
    try {
      const isUser = await User.findOne({
        where: {
          id,
        },
      });
      return `${isUser.name} ${isUser.surname}`;
    } catch (e) {
      /* just ignore */
    }
  }

  function setAuthorizationKey() {
    let authorizationKey = '';
    const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';

    for (let i = 0; i < 5; i++)
      authorizationKey += possible.charAt(
        Math.floor(Math.random() * possible.length),
      );

    return authorizationKey;
  }

  async function sendAuthorizationKey(
    senderId,
    recipientId,
    amountMoney,
    authorizationKey,
  ) {
    await nodemailer.createTestAccount();
    const transporter = nodemailer.createTransport({
      host: env.nodemailer.host,
      port: env.nodemailer.port,
      secure: false,
      auth: {
        user: env.nodemailer.username,
        pass: env.nodemailer.password,
      },
    });

    const mailOptions = {
      from: "example"`,
      to: `${await getSenderEmail(senderId)}`,
      subject: 'example',
      text: 'example`,
    };

    await transporter.sendMail(mailOptions);
  }

  function setTransferHistory(
    senderId,
    recipientId,
    amountMoney,
    transferTitle,
    authorizationKey,
  ) {
    Transaction.create({
      id_sender: senderId,
      id_recipient: recipientId,
      date_time: getTodayDate(),
      amount_money: amountMoney,
      transfer_title: transferTitle,
      authorization_key: authorizationKey,
      authorization_status: setAuthorizationStatus(0),
    });
  }

  Bill.findOne({
    where: {
      account_bill: req.body.account_bill,
    },
  }).then(isAccountBill => {
    if (isAccountBill) {
      const recipientId = isAccountBill.id_owner;
      const authorizationKey = setAuthorizationKey();
      const senderId = req.body.id_sender;
      const amountMoney = req.body.amount_money;
      const transferTitle = req.body.transfer_title;

      if (recipientId !== senderId) {
        Bill.findOne({
          where: {
            id_owner: senderId,
          },
        }).then(isAvailableFunds => {
          if (isAvailableFunds) {
            const senderAvailableFunds = isAvailableFunds.available_funds;

            if (senderAvailableFunds >= amountMoney && amountMoney > 0) {
              Transaction.findOne({
                where: {
                  id_sender: senderId,
                  id_recipient: recipientId,
                  amount_money: amountMoney,
                  transfer_title: transferTitle,
                  authorization_key: authorizationKey,
                  authorization_status: setAuthorizationStatus(0),
                },
                order: [['date_time', 'DESC']],
              }).then(isAuthorizationKey => {
                if (!isAuthorizationKey) {
                  setTransferHistory(
                    senderId,
                    recipientId,
                    amountMoney,
                    transferTitle,
                    authorizationKey,
                  );
                  sendAuthorizationKey(
                    senderId,
                    recipientId,
                    amountMoney,
                    authorizationKey,
                  );
                  return res.status(200).json({ success: true }); /* it should stop in this place */
                }
                return res.status(400).json({
                  error: 'Authorization key has been sent',
                  success: false,
                });
              });
            }
            return res.status(400).json({
              error: 'Sender does not have enough money',
              success: false,
            });
          }
          return res
            .status(404)
            .json({ error: 'Id sender doesnt exist', success: false });
        });
      }
      return res
        .status(404)
        .json({ error: 'Attempt payment to myself', success: false });
    }
    return res
      .status(404)
      .json({ error: 'Recipient does not exist', success: false });
  });
};

【问题讨论】:

  • 异步函数返回一个隐式的 Promise 对象,并且必须这样对待。从异步函数返回一些值没有意义:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • 以后请不要在您的帖子中包含“xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx” :)

标签: javascript node.js


【解决方案1】:

在 Promise 回调中调用 return 也不会从调用代码返回。这是您在简化示例中的代码

if (someCondition) {
  doSomethingAsync().then(() =>
    ...
    return res.status(200).json(...);
  });
}
return res.status(400).json(...);

现在给定doSomethingAsync 是异步的,您将传递一个回调函数 (then) 以便在 I/O 绑定进程完成时收到通知。但是,在此调用正在进行时,当前代码仍将运行到完成,因此会无意中调用return res.status(400).json(...),这意味着,当针对 I/O 绑定操作触发回调时,您已经结束了请求并因此收到适当的错误。

要解决此问题,您需要等待异步代码完成才能继续,这正是 async / await 的设计目的

if (someCondition) {
  await doSomethingAsync();
  return res.status(200).json(...);
}
return res.status(400).json(...);

注意 - 你的代码中确实有一些场景可以调用 async 函数,但不要 await 它们,例如sendAuthorizationKey,不确定这是否是故意的,但确实意味着代码很可能不会在剩余代码执行之前完成

【讨论】:

  • 我仍然不知道如何在我的代码中改进它。 :/ @詹姆斯
  • @ReactRouter4 你在纠结什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-02
  • 2020-07-12
  • 2023-04-04
  • 2017-09-08
  • 1970-01-01
  • 1970-01-01
  • 2017-12-20
相关资源
最近更新 更多