Thunk

So far all our Redux operations have been synchronous.

Some actions, such as data fetching requests, are generally asynchronous in nature and warrant the use of thunks for an elegant coding pattern.

Redux Thunk is a middleware that lets you call action creators that return a function instead of an action object. That function receives the store's dispatch method, which is then used to dispatch regular synchronous actions inside the function's body once the asynchronous operations have been completed.

When the ReduxThunk middleware sees the action dispatched is a function, it invokes the function, which accepts the dispatch function as the first parameter.
RESETRUNFULL
<!DOCTYPE html>
<html>
   <head>
      <title>Redux basic example</title>
      <script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/redux-thunk/2.4.1/redux-thunk.min.js"></script>
   </head>
   <body>
      <div>
         <p>
            Clicked: <span id="value">0</span> times
            <button onclick="store.dispatch(delayed_increment(2))">delayed +</button>
            <button onclick="store.dispatch(increment())">+</button>
         </p>
      </div>
      <script>
         function counter(state, action) {   // a reducer
            if (typeof state === 'undefined') return 0;
            switch (action.type) {
               case 'INCREMENT': return state + 1;
               case 'DECREMENT': return state - 1;
               default:          return state;
            }
         }
         function increment(){    // an action creator
            return {type:'INCREMENT'};
         }         
         function delay(sec){    // an asynchronous operation
            return new Promise(resolve=>{
               setTimeout(resolve,sec*1000);
            });
         }
         function delayed_increment(sec){   // a thunk
            return function (dispatch, getState){
               delay(sec).then(()=>dispatch({type:'INCREMENT'}));
            }
         }
         var store = Redux.createStore(counter, Redux.applyMiddleware(ReduxThunk));
         function render() { document.getElementById('value').innerHTML = store.getState().toString();}
         render();
         store.subscribe(render);
      </script>
   </body>
</html>
The ReduxThunk middleware can accept an extra parameter.
RESETRUNFULL
<!DOCTYPE html>
<html>
   <head>
      <title>Redux basic example</title>
      <script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/redux-thunk/2.4.1/redux-thunk.min.js"></script>
   </head>
   <body>
      <div>
         <p>
            Clicked: <span id="value">0</span> times
            <button onclick="store.dispatch(delayed_increment(2))">delayed +</button>
            <button onclick="store.dispatch(increment())">+</button>
         </p>
      </div>
      <script>
         function counter(state, action) {   // a reducer
            if (typeof state === 'undefined') return 0;
            switch (action.type) {
               case 'INCREMENT': return state + 1;
               case 'DECREMENT': return state - 1;
               default:          return state;
            }
         }
         function increment(){    // an action creator
            return {type:'INCREMENT'};
         }         
         function delay(sec){    // an asynchronous operation
            return new Promise(resolve=>{
               setTimeout(resolve,sec*1000);
            });
         }
         function delayed_increment(sec){   // a thunk
            return function (dispatch, getState, extraArg){
               delay(sec*extraArg).then(()=>dispatch({type:'INCREMENT'}));
            }
         }
         var store = Redux.createStore(counter, Redux.applyMiddleware(ReduxThunk.withExtraArgument(3)));
         function render() { document.getElementById('value').innerHTML = store.getState().toString();}
         render();
         store.subscribe(render);
      </script>
   </body>
</html>