Search code examples
javascriptpromisees6-promise

how to avoid the explicit-construction anti-pattern and still keep indentation/callbacks low


Lets say we have a function, that calls a fair amount of async functions like so:

downloadAndAssemble = () => 
  new Promise(async (resolve, reject) => {

    if (!(await checkAvailableDiskSpace())) {
      resolve(false);
      return;
    }

    try {
      // Download all
      let files  = await this.downloadAll();

      // Assemble File from downloaded snippets.
      const assembledFile = await buildFile(files).catch(error => reject(error));

      // Save assembled file.
      resolve(true);
    } catch (err) {
      reject(err);
    } finally {
      const dirExists = await fs.exists(tmpFolderPath);
      if (dirExists) await fs.unlink(tmpFolderPath);
    }
  });

The first problem I see is new Promise(async (resolve, reject) => { which is an anti pattern according to this SO article.

The general Idea I got from that article is to reuse Promises if available, and not create new ones.

If I follow the suggestions in this SO answer I should use .then and .catch in the logic flow of the function to utilize the existing Promises.

But this would result in more indentations (i.e. one per promise used), which I thought Promises should help eliminate.

As you can see from .catch(error => reject(error)) the code is not very consistent with handling errors thrown by Promises contained within it.


Solution

  •   downloadAndAssemble = async () =>  {
        if (!(await checkAvailableDiskSpace())) {
          return false;
        }
    
         try {
           // Download all
           let files  = await this.downloadAll();
    
           // Assemble File from downloaded snippets.
           const assembledFile = await buildFile(files);
    
          // Save assembled file.
          return true;
        } finally {
           const dirExists = await fs.exists(tmpFolderPath);
           if (dirExists) await fs.unlink(tmpFolderPath);
        }
    };
    

    If you call an async function, it will implicitly create a promise under the hood for you, that resolves if you return and rejects if you throw, so there is no need to create and manage a promise "manually".

    .catch(error => reject(error)) makes little sense as awaiting a promise automatically lets the errors bubble up. In your code this bypasses the try { ... } catch { ... } which is probably not wanted.

    The same applies to } catch (err) { reject(err); }, await is all you need.