What is the explicit promise construction antipattern and how do I avoid it?

The explicit promise construction antipattern, also known as the deferred antipattern or the Promise constructor antipattern, refers to the practice of using promise constructors such as Q.defer(), $q.defer(), new $.Deferred(), or new Promise() unnecessarily in JavaScript code. This antipattern is considered bad practice because it introduces unnecessary complexity and can lead to less efficient code.

Why is it an antipattern?

There are a few reasons why the explicit promise construction antipattern is discouraged:

  • Unnecessary Complexity: Using promise constructors adds unnecessary complexity to the code. It requires additional lines of code and can make the code harder to read and understand.
  • Inefficient Resource Allocation: Promise constructors create an extra promise object that is not needed. This can lead to inefficient resource allocation and negatively impact performance.
  • Potential for Error: Manually managing the resolution and rejection of promises using promise constructors increases the chances for error. It is easier to miss a resolve or reject statement, leading to unhandled promise rejections or incomplete code logic.

How to avoid the antipattern?

To avoid the explicit promise construction antipattern, you can follow these best practices:

1. Use Promise Chaining:

Instead of manually constructing promises, you can leverage the built-in methods for chaining promises. This allows you to simplify your code and reduces the chances of introducing errors. Here's an example:


function getStuffDone(param) {
    return myPromiseFn(param+1)
        .then(function(val) {
            return val;
        })
        .catch(function(err) {
            throw err;
        });
}
            

2. Utilize Async/Await:

If you're using a version of JavaScript that supports async/await (ES6+), you can further simplify your code by using async/await syntax. This allows you to write asynchronous code that looks like synchronous code. Here's an example:


async function getStuffDone(param) {
    try {
        const val = await myPromiseFn(param+1);
        return val;
    } catch (err) {
        throw err;
    }
}
            

3. Use Promise Libraries:

If you're working with older codebases or libraries that still use promise constructors, you can consider using third-party promise libraries like Bluebird or Q. These libraries provide additional functionality and can help simplify promise handling. Here's an example using Bluebird:


const Promise = require('bluebird');

function getStuffDone(param) {
    return Promise.resolve(myPromiseFn(param+1))
        .catch(function(err) {
            throw err;
        });
}
            

Conclusion

The explicit promise construction antipattern should be avoided as it introduces unnecessary complexity, inefficient resource allocation, and potential for error. By following best practices such as using promise chaining, async/await syntax, or utilizing promise libraries, you can write cleaner and more efficient code.