Redux Async Queue Middleware

raw JSON →
1.0.0 verified Thu Apr 23 auth: no javascript maintenance

redux-async-queue is a Redux middleware designed to manage the sequential execution of asynchronous actions. It allows developers to define multiple distinct queues, ensuring that actions belonging to a specific queue are processed one at a time, in the order they were dispatched. Each queued action dispatches a `callback` that receives `next`, `dispatch`, and `getState`, requiring an explicit call to `next()` to signal completion and allow the next item in the queue to proceed. This pattern is useful for operations that must be serialized, like sequential API calls or resource-intensive tasks that cannot run concurrently. The package is currently at version 1.0.0, suggesting a stable, though possibly no longer actively developed, state. Its differentiator lies in its explicit queue naming and manual progression mechanism, contrasting with more general-purpose async Redux patterns like thunks or sagas which handle concurrency differently or rely on more advanced paradigms. The library focuses solely on ordered async execution within a Redux store.

error TypeError: ReduxAsyncQueue is not a function
cause Attempting to use `require('redux-async-queue')` directly in CommonJS without accessing the default export.
fix
Change const ReduxAsyncQueue = require('redux-async-queue'); to const ReduxAsyncQueue = require('redux-async-queue').default;
error My Redux async actions are not executing in order or are getting stuck after the first one.
cause The `next()` callback was not invoked within the `callback` function of a queued action, preventing the queue from advancing.
fix
Ensure next() is called after your asynchronous operation completes within the callback: callback: (next, dispatch, getState) => { asyncOperation().then(() => { dispatch(someAction()); next(); }); }
gotcha When importing the middleware using CommonJS `require`, developers must access the `.default` property. Failing to do so will result in `TypeError: ReduxAsyncQueue is not a function` because the module's top-level export is the ES module wrapper, not the function itself.
fix Use `const ReduxAsyncQueue = require('redux-async-queue').default;` for CommonJS environments.
gotcha The `callback` function provided in a queued action *must* explicitly call `next()` to signal completion of the current asynchronous task. If `next()` is not called, the queue will halt indefinitely, and subsequent actions in that queue will never execute.
fix Ensure `next()` is called at the appropriate point within your `callback` function, typically after the async operation completes successfully.
gotcha The library's approach to async action management is somewhat foundational. With the evolution of Redux and the rise of Redux Toolkit, which integrates `redux-thunk` by default and has strong support for `redux-saga` or `redux-observable`, this middleware might not be the most idiomatic or integrated choice for new projects, especially those using Redux Toolkit. It represents an older pattern for managing sequential async operations.
fix Consider alternatives like `redux-thunk` (for simple async flows), `redux-saga` (for complex, side-effect heavy workflows), or `redux-observable` for reactive programming patterns, particularly if starting a new project with Redux Toolkit.
deprecated The package appears to be in maintenance mode or abandoned, with version 1.0.0 being the only or final major release documented. This suggests a lack of active development, potential compatibility issues with very recent Redux versions or TypeScript evolutions, and no new features or bug fixes. Security updates are unlikely.
fix Evaluate the package's GitHub repository for recent activity. For new projects, prefer actively maintained solutions. For existing projects, thoroughly test with new Redux versions.
npm install redux-async-queue
yarn add redux-async-queue
pnpm add redux-async-queue

This quickstart demonstrates how to set up `redux-async-queue` with a Redux store, define actions with a `queue` key and `callback`, and dispatch multiple asynchronous operations that will execute sequentially. It showcases the crucial `next()` call to advance the queue.

import { createStore, applyMiddleware } from 'redux';
import ReduxAsyncQueue from 'redux-async-queue';

// Basic Redux reducer (e.g., in reducers/index.js)
const initialState = { burgersMade: 0, burgerQueue: [] };
const reducer = (state = initialState, action) => {
  switch (action.type) {
    case 'MAKE_BURGER_COMPLETE':
      console.log(`Burger made: ${action.payload}`);
      return { ...state, burgersMade: state.burgersMade + 1, burgerQueue: [...state.burgerQueue, action.payload] };
    default:
      return state;
  }
};

// Create the Redux store with the async queue middleware
const store = createStore(
  reducer,
  applyMiddleware(ReduxAsyncQueue)
);

// Example function to simulate async ingredient fetching (e.g., an API call)
function getIngredients(style) {
  console.log(`Getting ingredients for ${style} burger...`);
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(`Ingredients ready for ${style} burger.`);
      resolve(`${style} burger`);
    }, Math.random() * 1500 + 500); // Simulate network delay
  });
}

// Action creator for the actual burger making
function makeBurgerComplete(burgerType) {
  return {
    type: 'MAKE_BURGER_COMPLETE',
    payload: burgerType,
  };
}

// Action creator to add a burger to the queue
function queueMakeBurger(style) {
  return {
    queue: 'BURGER_QUEUE', // Name of the queue
    callback: (next, dispatch, getState) => {
      console.log(`Starting to make a ${style} burger.`);
      getIngredients(style).then(burger => {
        dispatch(makeBurgerComplete(burger)); // Dispatch a regular Redux action
        next(); // IMPORTANT: Signal that this item in the queue is complete
      });
    }
  };
}

// Dispatch multiple actions to the queue
console.log('Dispatching burger requests...');
store.dispatch(queueMakeBurger('Cheeseburger'));
store.dispatch(queueMakeBurger('Bacon Burger'));
store.dispatch(queueMakeBurger('Veggie Burger'));

// Output current state after initial dispatches
console.log('Current state:', store.getState());

// Example: Monitor store for changes
store.subscribe(() => {
  console.log('Store updated:', store.getState().burgersMade, 'burgers made.');
});