Preventing unhandled promise rejections in async functions
Tags: javascript
Published: 2019-11-02 11:04:00

---
title: Preventing unhandled promise rejections in async functions
published: true
description: How to prevent unhandled promise rejections when using async/ await.
tags: JavaScript
cover_image: https://thepracticaldev.s3.amazonaws.com/i/4woqqyswhw358yqk3zbc.png
---

I am developing a HTTP proxy service and I have observed presence of an odd error message in my logs:

```
unhandledRejection RequestError: HTTP request error.
    at /dev/rayroute/raygun/src/factories/createRequest.js:107:13
    at processTicksAndRejections (internal/process/task_queues.js:93:5) {
  code: 'RAYGUN_REQUEST_ERROR',
  originalError: Error: test
      at /dev/rayroute/raygun/src/factories/createRequest.js:73:29
      at processTicksAndRejections (internal/process/task_queues.js:93:5)

```

It is odd because there are plethora of tests to ensure that all errors are handled. It is also odd because I have never never seen unhandled rejection while developing the service (only ever saw it in production logs).

The relevant code looks like this:

```js
const activeRequestHandler = createRequest(requestDefinition);

if (incomingMessage.socket) {
  incomingMessage.socket.on('close', () => {
    if (responseIsReceived) {
      log.trace('client disconnected after response');
    } else {
      log.debug('client disconnected');

      activeRequestHandler.abort(new Error('CLIENT_DISCONNECTED'));
    }
  });
}

try {
  await actions.afterRequestActions(
    context,
    requestDefinition,
    activeRequestHandler
  );
} catch (error) {
  log.error({
    error: serializeError(error),
  }, 'afterRequest threw an error');
}

try {
  responseDefinition = await activeRequestHandler.response;
} catch (error) {
  log.warn({
    error: serializeError(error),
  }, 'an error occurred while waiting for a HTTP response');

  // [..]
}

```

It is pretty straightforward:

* `createRequest` initiates a HTTP request and returns a request handler
* the request handler can be used to abort the ongoing request (`afterRequestActions` aborts request after a hard-timeout); and
* it is used to resolve the response or eventual rejection of the promise

I have written tests to ensure that errors are handled when:

* request rejected
* request aborted
* `afterRequestActions` throws an error

, but all tests are passing.

🤔

It turns out that the problem was that in all my test cases `actions.afterRequestActions` was resolving/ being rejected before `activeRequestHandler.response` is resolved. Meanwhile, in production `afterRequestActions` contains logic that can take substantially longer to execute. I have also learned that even if you declare a `try..catch` block for your async function, if it resolves before it is `await`-ted, then you will get an unhandled rejection, i.e.


This code will not warn about unhandled rejection:

```js
const delay = require('delay');

const main = async () => {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Error('Expected rejection.'));
    }, 100);
  });

  await delay(90);

  try {
    await promise;
  } catch (error) {
    console.error(error)
  }
};

main();


```

But this code will always produce a warning about an unhandled rejection:


```js
const delay = require('delay');

const main = async () => {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Error('Expected rejection.'));
    }, 100);
  });

  await delay(110);

  try {
    await promise;
  } catch (error) {
    console.error(error)
  }
};

main();


```

The best solution is to add an auxiliary catch block, e.g. This is how I refactored my original code:

```js
const activeRequestHandler = createRequest(requestDefinition);

// Without this we were getting occasional unhandledRejection errors.
// @see https://dev.to/gajus/handling-unhandled-promise-rejections-in-async-functions-5b2b
activeRequestHandler.response.catch((error) => {
  log.warn({
    error: serializeError(error),
  }, 'an error occurred while waiting for a HTTP response (early warning)');
});

if (incomingMessage.socket) {
  incomingMessage.socket.on('close', () => {
    if (responseIsReceived) {
      log.trace('client disconnected after response');
    } else {
      log.debug('client disconnected');

      activeRequestHandler.abort(new Error('CLIENT_DISCONNECTED'));
    }
  });
}

try {
  await actions.afterRequestActions(
    context,
    requestDefinition,
    activeRequestHandler
  );
} catch (error) {
  log.error({
    error: serializeError(error),
  }, 'afterRequest threw an error');
}

try {
  responseDefinition = await activeRequestHandler.response;
} catch (error) {
  log.warn({
    error: serializeError(error),
  }, 'an error occurred while waiting for a HTTP response');

  // [..]
}

```