Pup

v2.x

ActionsWith Promises

In some situations, it may be necessary to wait on the result of an action. In order to facilitate this without a lot of frustration, the action pattern can be modified to utilize JavaScript Promises.

On the right, we've taken the action pattern described here but have made some changes. First and most important, notice that the function being exported at the bottom of the file—the action caller—has been replaced with a call to a function returning a Promise.

This is the "magic." Here, we wrap the Promise around a function with the actionName which will serve as the new action caller function. To that function, we pass the options object from the outer wrapper function (this behaves identical to the non-promise version of actions).

Additionally, notice that a variable action is being set equal to an object containing the resolve and reject methods from the Promise we create. That variable is defined at the top of the file. This is done on purpose so that we can access/control the Promise from any of our action methods.

Last but not least, notice that while our action methods still rely on a regular JavaScript throw new Error() for their catch() block, the function we've added as our action caller uses action.reject(). The idea here is that this will halt the Promise and return the error from the step that failed to the original call's .catch() block.

To make sure that's clear, beneath our action definition, we can see an example putting it to use.

Example Action with Promises
/* eslint-disable consistent-return */

let action;

const actionMethodTwo = async (responseFromActionMethodOne) => {
  try {
    // Perform a single step in your action here.
  } catch (exception) {
    throw new Error(`[actionName.actionMethodTwo] ${exception.message}`);
  }
};

const actionMethodOne = (someOption) => {
  try {
    // Perform a single step in your action here.
  } catch (exception) {
    throw new Error(`[actionName.actionMethodOne] ${exception.message}`);
  }
};

const validateOptions = (options) => {
  try {
    if (!options) throw new Error('options object is required.');
    if (!options.someOption) throw new Error('options.someOption is required.');
  } catch (exception) {
    throw new Error(`[actionName.validateOptions] ${exception.message}`);
  }
};

const actionName = (options) => {
  try {
    validateOptions(options);
    const actionMethodOneResponse = actionMethodOne(options.someOption);
    const actionMethodTwoResponse = await actionMethodTwo(actionMethodOneResponse);
    action.resolve(actionMethodTwoResponse);
  } catch (exception) {
    action.reject(`[actionName] ${exception.message}`);
  }
};

export default (options) =>
  new Promise((resolve, reject) => {
    action = { resolve, reject };
    actionName(options);
  });
Example Usage
import actionName from './actionName';

actionName({ someOption: 'A userId or other variable' })
  .then((response) => {
    console.log(response);
  })
  .catch((error) => {
    console.warn(error);
  });