npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

redux-action-promise-enhancer

v1.2.10

Published

Create a promise from a redux store and a list of expected actions that will resolve in the future

Readme

Build Status Coverage Status NPM Package Version Package Size

Redux Action Promise

Listen to redux actions being dispatched outside redux reducers, create queues of actions.

  • Subscribe to actions in a redux store
  • Create a promise from a redux store with a list of expected actions that will resolve in the future
  • Return a promise from a dispatch on a redux store, with a dispatch action and a list of actions that will resolve or reject the promise
  • chain actions in a queue and execute them sequentially in a specific order, pause and resume the execution of the queue at will.

Why does this package exist?

When starting work on this, there was no easy way to chain actions in redux sequentially or to listen for a given action outside the context of the reducer in redux. This becomes a problem when working with any more complex processing of actions, that trigger async events, such as requesting an API based on an action, and dispatching an action with the result.

With this package you are able to listen for the result where the dispatch is made, and handle the result there as well as in the reducer, this gives you more flexibility in redux-saga or similar packages on handling these cases.

In addition, when a project gets to a certain size, it is difficult to ensure the order of execution of actions (for ex. if a dialogs are opened from one part of the application in sequence, it is not simple to add a dialog in the middle of the sequence, or to pause the execution of the dialogs and resume later).

Installation

npm install redux-action-promise-enhancer

or with yarn

yarn add redux-action-promise-enhancer

Usage

importing

import ActionPromiseEnhancer from 'redux-action-promise-enhancer';

or

import { ActionPromiseEnhancer } from 'redux-action-promise-enhancer';

using require

const ActionPromiseEnhancer = require('redux-action-promise-enhancer').default;

Composing with a redux store

Creation

const store: ActionPromiseStore = createStore(myReducer, ActionPromiseEnhancer);

if you have multiple enhancers, simply using the redux compose functionality

const store: ActionPromiseStore = createStore(myReducer, compose(ActionPromiseEnhancer, ...otherEnhancers));

API Documentation

Contents

Request Actions

Action Promises

Action Subscriptions

Action Queue

Request Actions

sometimes we want to dispatch an action, and we want to know when a following action has occurred where we dispatched our action, however, we can't easily do this with redux, this is where request actions come in, when you feed a request action to the dispatch function it will respond with a promise that you can await to get the payload of the subsequently dispatched action

import ActionPromiseEnhancer, { createRequestAction, ActionPromiseStore } from 'redux-action-promise-enhancer';

const MyActionType1 = 'my-action';
const MyResponseAction1 = {type: 'my-action-completed'};
const requestActionCreator = createRequestAction((payload: number) => ({
        type: MyActionType1,
        payload
    }), MyResponseAction1);

const store: ActionPromiseStore = createStore(myReducer, ActionPromiseEnhancer);

const dispatch = async () => {
    const response = await store.dispatch(requestActionCreator(1));
    
    console.log('awaited response', response);
};

store.subscribeToActions(MyActionType1).addListener((action) => {
    console.log('request action', action)
    store.dispatch(MyResponseAction1);
});

dispatch();

logs:

request action {
    type: 'my-action',
    payload: 1
}

awaited response { type: 'my-action-completed' }

handling errors in request actions

const MyRequestActionType1 = 'my-action';
const MyResponseErrorAction1 = {type: 'my-action-error'};
const requestAction = createRequestAction(
    {type: MyRequestActionType1},
    undefined,
    MyResponseErrorAction1
);

const dispatch = async () => {
    try {
        const response = await store.dispatch(requestAction);
    
        console.log('awaited response', response);
    } catch (e) {
        console.log('error while executing', e.rejectAction);
    }
};

store.subscribeToActions(MyRequestActionType1).addListener((action) => {
    console.log('request action', action)
    store.dispatch(MyResponseErrorAction1);
});

dispatch();

logs:

request action {
    type: 'my-action',
    payload: 1
}

error while executing { type: 'my-action-error' }

timeout a request action

const requestAction = createRequestAction(
    {type: MyRequestActionType1}, 
    MyResponseAction1,
    undefined,
    100
);
store.dispatch(requestAction)
    .catch((error: TimeoutError) => console.log(error.name, error.message));

Logs TimeoutError Timed out promise after 100ms, the promise is reject with an Error

Action Promises

Promises can be created from any enhanced redux store, that are resolved or rejected based on specified actions that will be dispatched in that store

import ActionPromiseEnhancer, { ActionPromiseStore } from 'redux-action-promise-enhancer';

const MyActionType1 = 'my-action';
const MyAction2 = {type: 'my-action-2'};
const store: ActionPromiseStore = createStore(myReducer, ActionPromiseEnhancer);

const logAction = async () => {
    console.log(await store.promise([MyActionType1, MyAction2]));
};

store.dispatch({
    type: MyActionType1,
    payload: 1
});

store.dispatch(MyAction2);

logAction();

store.dispatch({
    type: MyAction1,
    payload: 2
});

store.dispatch(MyAction2);

logs:

{
    type: 'my-action',
    payload: 2
}

{ type: 'my-action-2' }

If you want to reject the promise on a certain action, the action promise will be rejected with that action

const MyRejectAction1 = 'my-reject-action';
const store: ActionPromiseStore = createStore(myReducer, ActionPromiseEnhancer);
const logPromise = async () => {
    try {
        console.log(await store.promise([], [MyRejectAction1]))
    } catch (e: RejectActionErrorError) {
        console.log(e.rejectAction)
    }
};
logPromise();
store.dispatch({
    type: MyRejectAction1
});

logs {type: 'my-reject-action'}

Using the return promise object:

import ActionPromiseEnhancer, { ActionPromiseStore } from 'redux-action-promise-enhancer';

const MyAction1 = 'my-action';
const store: ActionPromiseStore = createStore(myReducer, ActionPromiseEnhancer);

const promise = store.promise([MyAction1]);
promise.then((action) => console.log(action));

store.dispatch({
    type: MyAction1,
    payload: 1
});
store.dispatch({
    type: MyAction1,
    payload: 2
});

logs:

{
    type: 'my-action',
    payload: 1
}

Promises can also be canceled, like so:

const promise = store.promise([MyAction1]);
promise.then((action) => console.log(action));
promise.cancel();
store.dispatch({
    type: MyAction1,
    payload: 1
});

logs:

If you want to specify a timeout for the generated promise, you can do that in ms as the final parameter of the promise method

const store: ActionPromiseStore = createStore(myReducer, ActionPromiseEnhancer);
const promise = store.promise([MyAction1], [], 100);
promise.catch((error: TimeoutError) => console.log(error.name, error.message));

Logs TimeoutError Timed out promise after 100ms, the promise is reject with an Error

Action Subscriptions

Subscriptions are used if you want to listen to one action type dispatching in a store, you can subscribe to any action that is dispatched in the store using this method, and listen for an action.

import ActionPromiseEnhancer, { ActionPromiseStore } from 'redux-action-promise-enhancer';

const MyAction1 = 'my-action';
const store: ActionPromiseStore = createStore(myReducer, ActionPromiseEnhancer);
const { addListener, unsubscribe } = store.subscribeToActions([MyAction1]);

addListener((action) => console.log('log 1', action));
const listener2 = addListener((action) => console.log('log 2', action));

store.dispatch({
    type: MyAction1,
    payload: 1
});

listener2.remove();

store.dispatch({
    type: MyAction1,
    payload: 2
});

unsubscribe();

store.dispatch({
    type: MyAction1,
    payload: 3
});

logs:

log 1 {
    type: 'my-action',
    payload: 1
}

log 2 {
    type: 'my-action',
    payload: 1
}

log 1 {
    type: 'my-action',
    payload: 2
}

You can also use action creator functions or action objects for any action array

import ActionPromiseEnhancer, { ActionPromiseStore } from 'redux-action-promise-enhancer';

const MyActionCreator1 = (payload) => {type: 'my-action', payload};
const store: ActionPromiseStore = createStore(myReducer, ActionPromiseEnhancer);
const { addListener, unsubscribe } = store.subscribeToActions([MyActionCreator1]);

addListener((action) => console.log(action));

store.dispatch(MyActionCreator1(1));

logs:

{
    type: 'my-action',
    payload: 1
}

Action Queue

A queue is used when you want actions to execute in a certain order in the store, so you can create a queue and dispatch the actions in the queue, waiting for end actions.

import ActionPromiseEnhancer, { ActionPromiseStore } from 'redux-action-promise-enhancer';

// creating a queue
const store: ActionPromiseStore = createStore(myReducer, ActionPromiseEnhancer);
const queue = store.createActionQueue();

Queueing multiple actions, with start and end action

// setup actions
const startAction1 = {type: 'startAction1'};
const endAction1 = {type: 'endAction1'};
const startAction2 = {type: 'startAction2'};
const endAction2 = {type: 'endAction2'};
const startAction3 = {type: 'startAction3'};
const endAction3 = {type: 'endAction3'};
const finalAction = {type: 'finalAction'};

// setup action listeners for logging
const { addListener: startAddListener1 } = store.subscribeToActions([startAction1]);
startAddListener1((action) => console.log(action));
const { addListener: endAddListener1 } = store.subscribeToActions([endAction1]);
endAddListener1((action) => console.log(action));
const { addListener: startAddListener2 } = store.subscribeToActions([startAction2]);
startAddListener2((action) => console.log(action));
const { addListener: endAddListener2 } = store.subscribeToActions([endAction2]);
endAddListener2((action) => console.log(action));
const { addListener: startAddListener3 } = store.subscribeToActions([startAction3]);
startAddListener3((action) => console.log(action));
const { addListener: endAddListener3 } = store.subscribeToActions([endAction3]);
endAddListener3((action) => console.log(action));
const { addListener: addListenerFinal } = store.subscribeToActions([finalAction]);
addListenerFinal((action) => console.log(action));

// queue up actions
queue.dispatch(startAction1, endAction1);
queue.dispatch(startAction2, endAction2);
queue.dispatch(startAction3, endAction3);
queue.dispatch(finalAction);

// dispatch the ending actions
store.dispatch(endAction3);
store.dispatch(endAction2);
store.dispatch(endAction1);
store.dispatch(endAction2);
store.dispatch(endAction1);
store.dispatch(endAction3);

logs:

{type: 'startAction1'}
{type: 'endAction3'}
{type: 'endAction2'}
{type: 'endAction1'}
{type: 'startAction2'}
{type: 'endAction2'}
{type: 'startAction3'}
{type: 'endAction1'}
{type: 'endAction3'}
{type: 'finalAction'}

Respects priority

// setup actions
const startAction1 = {type: 'startAction1'};
const endAction1 = {type: 'endAction1'};
const startAction2 = {type: 'startAction2'};
const endAction2 = {type: 'endAction2'};
const startAction3 = {type: 'startAction3'};
const endAction3 = {type: 'endAction3'};
const finalAction = {type: 'finalAction'};

// setup action listeners for logging
const { addListener: startAddListener1 } = store.subscribeToActions([startAction1]);
startAddListener1((action) => console.log(action));
const { addListener: endAddListener1 } = store.subscribeToActions([endAction1]);
endAddListener1((action) => console.log(action));
const { addListener: startAddListener2 } = store.subscribeToActions([startAction2]);
startAddListener2((action) => console.log(action));
const { addListener: endAddListener2 } = store.subscribeToActions([endAction2]);
endAddListener2((action) => console.log(action));
const { addListener: startAddListener3 } = store.subscribeToActions([startAction3]);
startAddListener3((action) => console.log(action));
const { addListener: endAddListener3 } = store.subscribeToActions([endAction3]);
endAddListener3((action) => console.log(action));
const { addListener: addListenerFinal } = store.subscribeToActions([finalAction]);
addListenerFinal((action) => console.log(action));

// queue up actions
queue.dispatch(startAction1, endAction1, undefined, 3);
queue.dispatch(startAction3, endAction3, undefined, 1);
queue.dispatch(startAction2, endAction2, undefined, 2);
queue.dispatch(finalAction);

// dispatch the ending actions
store.dispatch(endAction1);
store.dispatch(endAction2);
store.dispatch(endAction3);

logs:

{type: 'startAction1'}
{type: 'endAction1'}
{type: 'startAction2'}
{type: 'endAction2'}
{type: 'startAction3'}
{type: 'endAction3'}
{type: 'finalAction'}

Multiple queues

const queue1 = store.createActionQueue();
const queue2 = store.createActionQueue();

//action setup
const startAction1 = {type: 'startAction1'};
const startAction2 = {type: 'startAction2'};

//single end action
const endAction = {type: 'endAction'};

//multiple final actions
const finalAction1 = {type: 'finalAction1'};
const finalAction2 = {type: 'finalAction2'};

//add listeners
const { addListener: startAddListener1 } = store.subscribeToActions([startAction1]);
startAddListener1((action) => console.log(action));
const { addListener: finalAddListener1 } = store.subscribeToActions([finalAction1]);
finalAddListener1((action) => console.log(action));
const { addListener: startAddListener2 } = store.subscribeToActions([startAction2]);
startAddListener2((action) => console.log(action));
const { addListener: finalAddListener2 } = store.subscribeToActions([finalAction2]);
finalAddListener2((action) => console.log(action));
const { addListener: endAddListener } = store.subscribeToActions([endAction]);
endAddListener((action) => console.log(action));

//queue actions
queue1.dispatch(startAction1, endAction);
queue1.dispatch(finalAction1);

queue2.dispatch(startAction2, endAction);
queue2.dispatch(finalAction2);

//dispatch end action
store.dispatch(endAction);

logs:

{type: 'startAction1'}
{type: 'startAction2'}
{type: 'endAction'}
{type: 'finalAction1'}
{type: 'finalAction2'}

You can also use request actions in a queue

import {createAction} from "@reduxjs/toolkit";

//setup
const queue = store.createActionQueue();
const endAction1 = createAction('endAction1');
const requestAction1 = createRequestAction(createAction('startAction1'), endAction1);
const endAction2 = createAction('endAction2');
const requestAction2 = createRequestAction(createAction('startAction2'), endAction2);
const finalAction = createAction('finalAction');

//queue items
queue.dispatch(requestAction1);
const requestActionPromise = queue.dispatch(requestAction2);
queue.dispatch(finalAction);

//setup listeners
const { addListener: startAddListener1 } = store.subscribeToActions([requestAction1]);
startAddListener1((action) => console.log(action));
const { addListener: endAddListener1 } = store.subscribeToActions([endAction1]);
endAddListener1((action) => console.log(action));
const { addListener: startAddListener2 } = store.subscribeToActions([requestAction2]);
startAddListener2((action) => console.log(action));
const { addListener: endAddListener2 } = store.subscribeToActions([endAction2]);
endAddListener2((action) => console.log(action));
const { addListener: finalAddListener } = store.subscribeToActions([finalAction]);
finalAddListener((action) => console.log(action));

requestActionPromise.then(() => {
    console.log('request action 2 finished');
});

//dispatch end actions
store.dispatch(endAction1);
store.dispatch(endAction2);

logs:

{type: 'startAction1'}
{type: 'startAction2'}
{type: 'endAction1'}
{type: 'endAction2'}
request action 2 finished
{type: 'finalAction'}

Canceling queued actions

const startAction1 = {type: 'startAction1'};
const endAction1 = {type: 'endAction1'};
const startAction2 = {type: 'startAction2'};
const endAction2 = {type: 'endAction2'};
const startAction3 = {type: 'startAction3'};
const endAction3 = {type: 'endAction3'};
const finalAction = {type: 'finalAction'};

// setup action listeners for logging
const { addListener: startAddListener1 } = store.subscribeToActions([startAction1]);
startAddListener1((action) => console.log(action));
const { addListener: endAddListener1 } = store.subscribeToActions([endAction1]);
endAddListener1((action) => console.log(action));
const { addListener: startAddListener2 } = store.subscribeToActions([startAction2]);
startAddListener2((action) => console.log(action));
const { addListener: endAddListener2 } = store.subscribeToActions([endAction2]);
endAddListener2((action) => console.log(action));
const { addListener: startAddListener3 } = store.subscribeToActions([startAction3]);
startAddListener3((action) => console.log(action));
const { addListener: endAddListener3 } = store.subscribeToActions([endAction3]);
endAddListener3((action) => console.log(action));
const { addListener: addListenerFinal } = store.subscribeToActions([finalAction]);
addListenerFinal((action) => console.log(action));

// queue up actions
const promise1 = queue.dispatch(startAction1, endAction1);
const promise2 = queue.dispatch(startAction2, endAction2);
const promise3 = queue.dispatch(startAction3, endAction3);
const promise4 = queue.dispatch(finalAction);

// log when promises execute
promise1.then(() => console.log('action1 finished'));
promise2.then(() => console.log('action2 finished'));
promise3.then(() => console.log('action3 finished'));
promise4.then(() => console.log('finalAction finished'));

// cancel the 3th action
promise3.cancel();

//dispatch end actions
store.dispatch(endAction1);
store.dispatch(endAction2);
store.dispatch(endAction3);

logs:

{type: 'startAction1'}
{type: 'endAction1'}
action1 finished
{type: 'startAction2'}
{type: 'endAction2'}
action2 finished
{type: 'finalAction'}
finalAction finished
{type: 'endAction3'}

Canceling queued action while executing it

const startAction1 = {type: 'startAction1'};
const endAction1 = {type: 'endAction1'};
const startAction2 = {type: 'startAction2'};
const endAction2 = {type: 'endAction2'};
const startAction3 = {type: 'startAction3'};
const endAction3 = {type: 'endAction3'};
const finalAction = {type: 'finalAction'};

// setup action listeners for logging
const { addListener: startAddListener1 } = store.subscribeToActions([startAction1]);
startAddListener1((action) => console.log(action));
const { addListener: endAddListener1 } = store.subscribeToActions([endAction1]);
endAddListener1((action) => console.log(action));
const { addListener: startAddListener2 } = store.subscribeToActions([startAction2]);
startAddListener2((action) => console.log(action));
const { addListener: endAddListener2 } = store.subscribeToActions([endAction2]);
endAddListener2((action) => console.log(action));
const { addListener: startAddListener3 } = store.subscribeToActions([startAction3]);
startAddListener3((action) => console.log(action));
const { addListener: endAddListener3 } = store.subscribeToActions([endAction3]);
endAddListener3((action) => console.log(action));
const { addListener: addListenerFinal } = store.subscribeToActions([finalAction]);
addListenerFinal((action) => console.log(action));

// queue up actions
const promise1 = queue.dispatch(startAction1, endAction1);
const promise2 = queue.dispatch(startAction2, endAction2);
const promise3 = queue.dispatch(startAction3, endAction3);
const promise4 = queue.dispatch(finalAction);

// log when promises execute
promise1.then(() => console.log('action1 finished'));
promise2.then(() => console.log('action2 finished'));
promise3.then(() => console.log('action3 finished'));
promise4.then(() => console.log('finalAction finished'));

//dispatch end actions
store.dispatch(endAction1);
// cancel the 2nd action
promise2.cancel();
store.dispatch(endAction2);
store.dispatch(endAction3);

logs:

{type: 'startAction1'}
{type: 'endAction1'}
action1 finished
{type: 'startAction2'}
{type: 'startAction3'}
{type: 'endAction2'}
{type: 'endAction3'}
action3 finished
{type: 'finalAction'}
finalAction finished

Pausing and resuming the queue

// setup actions
const startAction1 = {type: 'startAction1'};
const endAction1 = {type: 'endAction1'};
const startAction2 = {type: 'startAction2'};
const endAction2 = {type: 'endAction2'};
const startAction3 = {type: 'startAction3'};
const endAction3 = {type: 'endAction3'};
const finalAction = {type: 'finalAction'};

// setup action listeners for logging
const { addListener: startAddListener1 } = store.subscribeToActions([startAction1]);
startAddListener1((action) => console.log(action));
const { addListener: endAddListener1 } = store.subscribeToActions([endAction1]);
endAddListener1((action) => console.log(action));
const { addListener: startAddListener2 } = store.subscribeToActions([startAction2]);
startAddListener2((action) => console.log(action));
const { addListener: endAddListener2 } = store.subscribeToActions([endAction2]);
endAddListener2((action) => console.log(action));
const { addListener: startAddListener3 } = store.subscribeToActions([startAction3]);
startAddListener3((action) => console.log(action));
const { addListener: endAddListener3 } = store.subscribeToActions([endAction3]);
endAddListener3((action) => console.log(action));
const { addListener: addListenerFinal } = store.subscribeToActions([finalAction]);
addListenerFinal((action) => console.log(action));

// queue up actions
queue.dispatch(startAction1, endAction1);
queue.dispatch(startAction2, endAction2);
queue.dispatch(startAction3, endAction3);
queue.dispatch(finalAction);

// dispatch the ending actions
store.dispatch(endAction1);
queue.pauseQueue();
console.log('queue paused');
store.dispatch(endAction2);
store.dispatch(endAction3);
queue.resumeQueue();
console.log('queue resumed');
store.dispatch(endAction3);

logs:

{type: 'startAction1'}
{type: 'endAction1'}
{type: 'startAction2'}
queue paused
{type: 'endAction2'}
{type: 'endAction3'}
queue resumed
{type: 'startAction3'}
{type: 'endAction3'}
{type: 'finalAction'}

Validation Mode:

The action promise enhancer validates the input it is given on each function, this ensures unique inputs of actions to avoid duplication entries or similar errors.

To disable this you can set the validation mode to 'compiletime' like so:

import ActionPromiseEnhancer, { ValidationMode } from 'redux-action-promise-enhancer';

ActionPromiseEnhancer.validationMode = ValidationMode.COMPILETIME;

note: you can only do this before the ActionPromiseEnhancer is used by a redux store, and will be used as a configuration for the app.

Plain JS example:

const MyAction1 = 'my-action';
const store = createStore(myReducer, ActionPromiseEnhancer);
const promise = store.promise([MyAction1]);
promise.then((action) => console.log(action));
store.dispatch({
    type: MyAction1
});

logs {type: 'my-action'}

License

MIT