Many a times, we come across a use case where a block of code needs to be executed only after certain defined period of time. For an instance, in an input text raw-value event handler, we would like to invoke REST API only after the user has stopped typing for a certain period. 

Let's consider we have a requirement to implement a solution where user wants to keep on typing in an input text to filter the data and display the resuts in table. If we use the code in the raw-value event listener as is then the REST API to filter the data will be invoked multiple times as user keeps on typing or even when user clears the search characters using the backspace.

The more optimal solution should be, we should invoke the REST API only after the user has stopped typing for a certain period of time. Using this approach we can:

  • Reduce significant load on the server by preventing unnecessary API calls.
  • Improves user experience.

Step 1: Create a variable to store setTimeout reference

Create a variable scoped at the application level of type Number. (Please note based on the use case this variable can be created at the flow or page level as well.) 

This variable will be used later to clear the timeout.


Step 2: Write Custom JavaScript Debounce Function:

Create a custom JavaScript function scoped at the application level. (Please note based on the use case we can create the function at the flow or page level as well.) 

This debounce function returns a wrapper function which will be executed at defined period of time. It also keeps track of any existing timeout call so that before returning a new wrapper function the current timeout call that is in queue to be executed is cleared. So, even though multiple events are trigged, we will always have one and only one event handlers in queue to be executed.

Debounce function accepts three parameters:

  • callback: This is the callback function which contains block of code that needs to be executed at a defined period of time.
  • delay: This is the time in milliseconds after which the callback function will be executed.
  • appVar: This is the reference to the $application.variables object so that we can refer the variable created in step 1.
define([], () => {
  'use strict';
  class AppModule {
    debounce(callback, delay, appVar){
    return (...args) => {
      if (appVar.timeoutid) clearTimeout(appVar.timeoutid);
      appVar.timeoutid = setTimeout(() => {
        callback(...args);
      }, delay);
    };
  };
}
  return AppModule;
});

Step 3: Input Text raw-value event Action Chain

Drag and drop an input text component and create an event listener which listen for raw-value event.

Below activities are performed in the raw-value event listener Action Chain:

  • REST API invocation to filter data is wrapped inside a function (invokeAPI) so that we can pass this as a callback to the debounce function created in step 2.
  • Debounce function created in step 2 is invoked to return a wrapper function which will be executed after 300 milliseconds. 
  class FilterEmps extends ActionChain {
    /**
     * @param {Object} context
     * @param {Object} params
     * @param {any} params.rawValue
     */
    async run(context, { rawValue }) {
      const { $page, $flow, $application, $constants, $variables } = context;
      const debounced = $application.functions.debounce(this.invokeAPI, 300,  $application.variables);
      const response = debounced(context, rawValue);
      if(response?.body?.items) $page.variables.empsADP.data = response.body.items;
    }

    async invokeAPI(context, rawValue) {
        return  Actions.callRest(context, {
        endpoint: 'businessObjects/getall_Emps',
        uriParams: {
          onlyData: true,
          q: "lastName='"+rawValue+"'",
        },
      });
    }
  }

Conclusion:

In this blog we have seen, how we can execute a block of code in an Action Chain only after a certain defined period of time. This will reduce significant load on the server by preventing unnecessary API calls and will improve the overall user experience.