NodeJS 挂在第二个请求上并且不发送响应

时间:2021-07-29 14:30:52

标签: node.js

嘿,我能得到一些帮助吗我不明白为什么当我点击同一条路线超过 1 次时,我的服务器无法发送响应。

所以我有一个 NodeJS 服务器,在那里我有一个事件发射器设置,用于发送类似于下面的电子邮件通知(另一个失败):

import EventEmitter from 'events';
import { createTransport, Transporter, SendMailOptions } from 'nodemailer';
import { Response } from 'express';
export const emitter = new EventEmitter();
emitter.on('email-notification', (message: SendMailOptions) => {
  const transpoter: Transporter = createTransport({
    service: 'SendinBlue',
    auth: {
      user: process.env.sendin_blue_login,
      pass: process.env.sendin_blue_pass,
    },
  });
  transpoter.sendMail(message, (error, info) => {
    if (error) {
      emitter.emit('email-notification-failed', error);
    }
    emitter.emit('email-notification-success', info);
  });
});

现在我有一条路线 PUT: /api/shipment/status/:order_id/:status,我想在第一次请求时更新发货状态,一切运行顺利我收到我的电子邮件通知,服务器也根据电子邮件成功发送时给出的发射器结构发送响应或不是 但是当我发送第二个或更多请求时出现问题,我收到了发送的电子邮件通知,但现在问题是服务器挂起并且没有回复响应 以下是我如何处理状态更新的控制器

async update_package_status(request: any, response: Response) {
    /**
     *STATUS -> 1: PENDING
             -> 2: CANCELLED
             -> 3: COMING
             -> 4: ARRIVED  
    **/

    const status =
      request.params.status === '1'
        ? ShipmentStatus.PENDING
        : request.params.status === '2'
        ? ShipmentStatus.CANCELLED
        : request.params.status === '3'
        ? ShipmentStatus.COMING
        : ShipmentStatus.ARRIVED;

    const order_id = request.params.order_id;

    try {
      const updatedDoc = await shipmentModel.findOneAndUpdate({ _id: order_id }, { $set: { status: status } }, { new: true });

      const user = await userModel.findOne({ _id: updatedDoc.owner });

      const html = `

      <h1/>Your package status has changed</h1>
      <ul style="list-style:none;">
        <li/>Current status: ${updatedDoc.status}</li>
        <li/>Current location: ${updatedDoc.current_location}</li>
        <li />Previous location: ${updatedDoc.travelled_locations[updatedDoc.travelled_locations.length - 2]}
        <li style="font-weight:bolder;"/>Shipment Total: ${updatedDoc.price}</li>
      </ul>
    `;

      const message: SendMailOptions = {
        from: 'noreply@sstoplogistics.com',
        to: user.email,
        subject: 'Package status update',
        html,
        priority: 'high',
        replyTo: 'help@sstoplogitics.com',
      };

      emitter.on(Event.EMAIL_NOTIFICATION_FAIL, (error) => {
        console.log('FAILED TO SEND EMAIL !!!! @STATUS UPDATE');
        return response.status(500).json({ msg: `Network Error: Failed to notify ${user.email} of their account being created` });
      });
      emitter.on(Event.EMAIL_NOTIFICATION_SUCCESS, (info) => {
        console.log('EMAIL SENT @STATUS UPDATE');
        return response.status(200).json({ msg: `Email notification sent to ${user.email} for status update` });
      });
    } catch (error) {
      return response.status(500).json(error);
    }
  }

1 个答案:

答案 0 :(得分:1)

虽然我不知道究竟是什么导致了您的错误,但有几件事引起了我的注意:

  • 每次 update_package_status 调用,您都会添加两个事件侦听器 你的emitter。你不仅从不删除它们,而且这些处理程序 还将接收来自其他尝试的成功/失败事件 发送邮件。
  • 您定义了 message 但我实际上从未看到您发出 email-notification 事件。

对于第一个问题,你可以让你的发送者成为一个异步函数:

async function sendEmail(message: SendMailOptions) {
    const transporter: Transporter = createTransport({
        service: 'SendinBlue',
        auth: {
            user: process.env.sendin_blue_login,
            pass: process.env.sendin_blue_pass,
        },
    });
    return new Promise((resolve, reject) => {
        transporter.sendMail(message, (error, info) =>
            error ? reject(error) : resolve(info));
    });
}

然后您可以使用 update_package_statustry/catch.then(info => {}).catch(error => {}) 中使用。

或者,如果您真的想要采用基于事件的方法,请在初始事件发出期间传递一个唯一 ID,然后您将在成功/失败事件处理程序中侦听该 ID。在这里,我将“如何处理正确的事件”部分包装在一个异步函数中,您可以根据自己的需要进行调整:

function sendEmail(message: SendMailOptions) {
    return new Promise((resolve, reject) => {
        // Could use an external counter you always increase, but this should be fine
        // if you don't send two emails to the same person in the same millisecond.
        const uniqueId = `${message.to}-${Date.now()}`;
        const makeHandler = (callback: any) => (data: SendMailReceipt | Error, forUniqueId: string) => {
            // Replace the SendMailReceipt here ^ with the proper type
            if (forUniqueId !== uniqueId) return; // Not in response to our attempt
            // Disconnect our event listeners, don't need them anymore
            emitter.off(Event.EMAIL_NOTIFICATION_SUCCESS, onSuccess);
            emitter.off(Event.EMAIL_NOTIFICATION_FAIL, onFail);
            callback(info);
        };
        const onSuccess = makeHandler(resolve);
        const onFail = makeHandler(reject);
        // Actually add the listeners
        emitter.on(Event.EMAIL_NOTIFICATION_SUCCESS, onSuccess);
        emitter.on(Event.EMAIL_NOTIFICATION_FAIL, onFail);
        // Actually emit the event to try to attempt the mail now
        // (doing it before adding our event listeners should be fine because
        // it shouldn't all be synchronous, but just in case)
        emitter.emit(Event.EMAIL_NOTIFICATION, message, uniqueId);
    });
}

假设您还在 uniqueId 回调中发出 sendMail