0

I'm new to Node.js and I'm trying to code two nested try/catch and put retry logic for it. So when inner try/catch catches error I want it to send to outer catch and inside of it I will increase retry count by 1. So when it comes to 5 I will return from while loop. But my problem is that when inner try/catch throws an exception it is not caught by outer one. How can I make sure that it catches the error?

    try {
      channel.assertQueue(name, { durable: true, arguments: { "x-queue-type": "quorum" } }, async (error, queue) => {
        if (error)
          throw error;

        if (queue) {
          try {
            channel.sendToQueue(name, Buffer.from(message));
          } catch (e) {
            console.log(e);
            throw e;
          }
        }
      });
    } catch (e) {
      //retry count will be increased.
      throw e.message;
    }

meursault
  • 255
  • 1
  • 4
  • 13
  • Because your inner throw is for the function which is defined inside assertQueue. To catch it outside, you need to throw it outside the body of assertQueue. – Hazik Arshad May 11 '23 at 10:33
  • I'm sorry I didn't get your response so you're saying inner try/catch is not needed? I have also tried that but still outer one can't catch error if sendToQueue throws an exception – meursault May 11 '23 at 10:43
  • i don't think you need to use multiple try catch, I always use only one try catch and it works perfectly – codewithsg May 11 '23 at 10:47
  • I think this doesn't work because when sendToQueue throws an error since it's inside of async promise outer try/catch can't catch it. It's definitely not working in this case – meursault May 11 '23 at 10:49

2 Answers2

1

To ensure that the outer catch block catches the error thrown in the inner try/catch block, you can modify your code as follows:

let retryCount = 0;

async function attemptToSendMessage() {
  while (retryCount < 5) {
    try {
      await new Promise((resolve, reject) => {
        channel.assertQueue(
          name,
          { durable: true, arguments: { "x-queue-type": "quorum" } },
          async (error, queue) => {
            if (error) {
              reject(error); // Reject the promise if an error occurs
              return;
            }

            if (queue) {
              try {
                channel.sendToQueue(name, Buffer.from(message));
                resolve(); // Resolve the promise if the message is sent successfully
              } catch (e) {
                reject(e); // Reject the promise if an error occurs while sending the message
              }
            }
          }
        );
      });
      break; // Exit the while loop if the message is sent successfully
    } catch (e) {
      console.log(e);
      retryCount++; // Increase the retry count
    }
  }

  if (retryCount === 5) {
    throw new Error("Failed to send message after 5 attempts.");
  }
}

// Usage
try {
  await attemptToSendMessage();
} catch (e) {
  // Handle the error here
  console.log(e.message);
}
1

You shouldn't pass a callback to channel.assertQueue, and not an async one for certain, instead you should promisify it. With await, you can then catch the errors in the outer try/catch.

You're probably looking for

async function attemptToSendMessage() {
  for (let retryCount = 0; retryCount < 5; retryCount++) {
    try {
      const queue = await new Promise((resolve, reject) => {
        channel.assertQueue(
          name,
          { durable: true, arguments: { "x-queue-type": "quorum" } },
          (error, queue) => {
            if (error) reject(error);
            else resolve(queue);
          }
        );
      });
      if (queue) {
        await channel.sendToQueue(name, Buffer.from(message));
      }
      return; // Exit the loop if the message is sent successfully
    } catch (e) {
      console.error(e);
    }
  }
  throw new Error("Failed to send message after 5 attempts.");
}

However, assuming you are using this API, you don't even need to promisify the assertQueue function yourself - it already returns a promise if you don't pass a callback. So simplify further to

async function attemptToSendMessage() {
  for (let retryCount = 0; retryCount < 5; retryCount++) {
    try {
      const queue = await channel.assertQueue(
        name,
        { durable: true, arguments: { "x-queue-type": "quorum" } },
      );
      if (queue) {
        await channel.sendToQueue(name, Buffer.from(message));
      }
      return; // Exit the loop if the message is sent successfully
    } catch (e) {
      console.error(e);
    }
  }
  throw new Error("Failed to send message after 5 attempts.");
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • This code also worked for me, thank you so much but what's the difference between two codes :) I have tested the case where sendToQueue fails and both codes worked like I want it – meursault May 11 '23 at 13:34
  • 1
    It's more straightforward, doesn't require to explicitly `reject()` on errors from `sendToQueue`, and will still work when `if (queue)` does not match – Bergi May 11 '23 at 13:37
  • Yes, I'm using this API but queue value which is returning in both of the codes are different. First oen returns sth like this : { queue: 'foobar', messageCount: 0, consumerCount: 0 } But second one is extremely detailed response. – meursault May 11 '23 at 14:04
  • I really appreciate your support btw, thank you so much :) – meursault May 11 '23 at 14:06