1

Is it good practice to wrap a try/catch with a Promise? I was reviewing the code below, I cannot seem to understand the necessity of having a try/catch block inside a Promise. Also, why would there be no place where the Promise rejects, as you can see in the catch block, the Promise resolves. Please help me understand this piece of code, in terms of best practice and efficiency the point of having the try/catch inside the Promise.

I would also really appreciate any suggestions regarding cleaning the code up where it is redundant.

getRoute(): any {
    return async (request: any, reply: any) => {
      const result = new Promise<string>( async (resolve, reject) => {
        try {
          let userIsValid = await this.validator.validate(request.payload, RegisterUserValidator);
          if (userIsValid.error) {
            resolve(responseHelper.getErrorResponse(ResponseErrorCode.joiValidatorError, userIsValid.error));
            throw new ControlFlowNonError();
          }
          let results = await this.authUser.registerUser(request.payload);
          resolve(responseHelper.getSuccessResponse(results, null));
        }
        catch (error) {
          let message: ILogMessage = {
            code: ResponseErrorCode.unknownError,
            message: ResponseErrorCode.unknownError.toString(),
            meta: error,
            sourceFunction : 'ApiDataCreateCredentials: getRoute()'
          };
          this.logHelper.error(message);
          resolve(error);
        }
      });
      reply(result);
    };
  };
Siya Mzam
  • 4,655
  • 1
  • 26
  • 44
  • Why would you `resolve()` and then `throw`? What are you trying to accomplish with that? That whole code flow you have is very odd (calling `resolve()` multiple times, throwing just so you can log something) and I would certainly not think it is a best practice.. – jfriend00 Nov 04 '17 at 16:17
  • This is also a [promise anti-pattern](https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns). You've surrounded an existing promise with `new Promise()`. The outer promise is not needed at all. You can just return the promise you already have. Unfortunately, I don't know TypeScript well enough to rewrite it for you. – jfriend00 Nov 04 '17 at 16:20
  • `resolve(error)` - are you sure you want that? – Bergi Nov 04 '17 at 16:34
  • @Bergi - Do you really think just referring the OP to another related post (that only covers some of what they're doing) is as useful to both the OP and the community as rewriting their code a better way and explaining what you did and why? I would not say that all the issues involved here are an "exact duplicate" of those other questions. – jfriend00 Nov 04 '17 at 16:51
  • What is `reply()` supposed to be given as an argument? Is it supposed to be given a promise? Or the resolved value of your promise? – jfriend00 Nov 04 '17 at 16:55
  • @jfriend00 Not all of them, but the title literally asks about `try`/`catch` (and imo, by extension, about `async`/`await`) in the `new Promise` constructor. Feel free to reopen if you want. – Bergi Nov 04 '17 at 17:09
  • This is code that was written by our lead developer. I was reviewing it since I also have to contribute to the project. I asked him about the oddness of the code (Promise in a Promise, resolving multiple times, resolving and throwing an error). He said, "The code will not work without having a `try` `catch`, since we use `await` calls and they run individually. To get the entire code to `reject`/ `resolve` in one go will be a mess, you need to have more callbacks to `resolve` and `reject` errors." – Siya Mzam Nov 05 '17 at 05:52
  • @Bergi, I don't think this is a duplicate. Those other references cover part of what I am concerned about. As jfriend00 mentioned, there is a lot of issues involved with this code that is not necessarily discussed in the other posts. Also, I think, it would be a really long, tiring question if I were to ask it again, mentioning all my concerns with the code in question. – Siya Mzam Nov 05 '17 at 06:09

1 Answers1

2

Is it best practice to have a try/catch block inside a promise [executor function]?

No, it's not best practice. There's pretty much no reason to use promises inside a promise executor function. Because when you're doing that, you don't need the outer, manually created promise at all. You can just return the inner promise. That's the Promise constructor anti-pattern.

FYI, though it isn't your case here, it is reasonable to use try/catch to handle regular exceptions inside a promise executor (if the manually created promise executor is actually needed in the first place and if regular exceptions are of concern and you want to handle them locally).


Here's another idea for how to accomplish your same logic. This removes the promise anti-pattern of surrounding a promise with another manually created one and uses promise flow control to make userIsValid.error go to the log error code. I didn't see any particular advantage of using await here so I switched back to just using .then() and .catch(). I don't know TypeScript so this is a regular Javascript version of the code, but you can presumably add the slight syntax differences yourself to turn it back to TypeScript:

getRoute(): function() {
    return function(request, reply) {
        return this.validator.validate(request.payload, RegisterUserValidator).then(userIsValid => {
            if (userIsValid.error) {
                // treat this as an error condition
                throw responseHelper.getErrorResponse(ResponseErrorCode.joiValidatorError, userIsValid.error);
            }
            return this.authUser.registerUser(request.payload).then(results => responseHelper.getSuccessResponse(results, null));
        }).catch(error => {
          let message: ILogMessage = {
            code: ResponseErrorCode.unknownError,
            message: ResponseErrorCode.unknownError.toString(),
            meta: error,
            sourceFunction : 'ApiDataCreateCredentials: getRoute()'
          };
          this.logHelper.error(message);
          // turn back into resolved promise with error as the result
          return error;
        }).then(reply);        // always call reply
    }
}    

Things that did not seem ideal with your implementation:

  1. Promise anti-pattern (creating an unnecessary wrapper promise).
  2. Calling resolve() multiple times (the second one will be ignored, but it seems less than ideal to code it that way)
  3. There does not seem to be any particular benefit to using async/await here. It seems to complicate the flow (in my opinion).
  4. resolve() followed by throw is a real head scratcher when reading the code. Yes, one can eventually figure out what you're trying to do, but this is an odd way to do it. My scheme is a bit more semantic. if (userIsValid.error) is really just an error condition for you so coding it this way just makes that more obvious. Then, since you want ALL errors to proceed as if there was no error (after logging), we just make the .catch() handler return normally which allows the last .then() handler to always get called.

Other references related to your topic:

Can't throw error from within an async promise executor function

Is it an anti-pattern to use async/await inside of a new Promise() constructor?

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • If you don't want to use `await` with `try`/`catch`, you might as well remove the `async` :-) – Bergi Nov 04 '17 at 16:33
  • @Bergi - Yep, meant to take it out. – jfriend00 Nov 04 '17 at 16:35
  • @jfriend00 This answer is very clear, I appreciate it a lot. I was struggling to explain this to my lead, but I knew for a fact that something was really odd with the code. I have not tested the suggestion you made. I will, tomorrow when I get to the office. But it should work as far as I am concerned. But I doubt if the code will get to production because our lead developer (he wrote that code) does not see anything wrong with the way he did things. He has reasons, that I do not necessarily agree with, why he wrote it that way. But I could not seem to convince him otherwise. – Siya Mzam Nov 05 '17 at 05:57
  • @fshock - For starters, have him read https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it and https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns about the Promise construction anti-pattern. It's just a wrong way to write promise code. – jfriend00 Nov 05 '17 at 06:25
  • I, certainly, will. Thanks, @jfriend00, I appreciate the help. – Siya Mzam Nov 05 '17 at 06:28