Linking functions together in a customized iteration sequence that accommodates both sync and async operations

I've created a for_users function that retrieves an array of users from a web service, applies a specified function f to each user in the array, and then triggers a callback function f_then.

// Apply f to each user, then trigger f_then.
function for_users(f, f_then)
{
    // Retrieve all users from the database and store them in user_array
    db.get_all_users(function(user_array)
    {
        // Apply f to each user
        user_array.forEach(f);

        // Call the continuation callback
        f_then();
    });
}

When using the for_users function with an asynchronous function as the parameter for f, I want all the f callbacks to complete before calling f_then. However, this isn't happening in the current setup because user_array.forEach(f) doesn't wait for f to finish before moving on to the next iteration.

Let's consider a problematic scenario:

function example_usage()
{
    var temp_credentials = [];

    for_users(function(user)
    {
        // The get_credentials function is asynchronous and will
        // invoke the provided callback after fetching credentials
        // from the database
        database.get_credentials(user.ID, function(credential)
        {
            // ...
        });
    }, function()
    {
        // The do_something call happens before all callbacks are
        // completed (potentially)

        // temp_credentials may be empty at this point!
        do_something(temp_credentials);
    });
}

How can I modify the for_users function so that if f is asynchronous, f_then is only triggered when all f functions have finished?

Sometimes, however, the provided f function for for_users is synchronous and the existing implementation might work. Is there a way to create a generic for_users function that accommodates both asynchronous and synchronous f functions?

Answer №1

Here is a solution that should meet your needs:

function for_users(f, f_then) {

  db.get_all_users(function(user_array) {
      var promises = [];

      user_array.forEach(function(user) {
        promises.push(new Promise(function(resolve, reject) {
          f(user);
          resolve();
        }));
      });

      if (f_then)
        Promise.all(promises).then(f_then);
      else
        Promise.all(promises);
    }
  });
}

Below is a simple test to try out the function:

function for_users(f, f_then) {
  var users = [{ID: 1}, {ID: 2}, {ID: 3}];
  var promises = [];

  users.forEach(function(user) {
    var promise = new Promise(function(resolve, reject) {
      f(user);
      resolve();
    });
    promises.push(promise);
  })

  if (f_then)
    Promise.all(promises).then(f_then);
  else
    Promise.all(promises)
}

for_users(function(user) {
  console.log(user.ID);
}, function() {
  console.log('finshed')
})

Answer №2

To implement a continuation callback in the function f, you can do the following:

function for_users(f, f_then) {
    // Retrieve all users from the database and store them in user_array
    db.get_all_users(function(user_array) {
        // Perform f on each user
        (function recur(next) {
            var user = user_array.shift();
            if (user) {
                f(user, function() {
                    recur(next);
                });
            } else {
                // Invoke the continuation callback
                next();
            }
        })(f_then);
    });
}

You can then use this function like so:

for_users(function(user, next) {
    // An asynchronous function get_credentials is called for fetching credentials
    // It invokes the provided callback upon completion
    database.get_credentials(user.ID, function(credential) {
        next();
    });
}, function() {
    // The function do_something is executed even before all callbacks are completed

    // temp_credentials might be empty at this point!
    do_something(temp_credentials);
});

Answer №3

function obtainUserCredentials(step){
    return function(user){
        database.fetchCredentials(user.ID, function(credential) {
            step(credential);
        });
    };
};

function processAllCompletion(f){
    return function(step) {
        return function(arr){
            var finished = 0;
            var values = new Array(arr.length);
            if(arr.length){
                arr.forEach(function(el, i){
                    f(function(value){
                        if(finished === arr.length){
                            step(values);
                        } else {
                            values[i] = value;
                            finished++;
                        }
                    })(el);
                });
            } else {
                step(values);
            }
        };
    };  
};

function iterateThroughUsers(doSomething){
    db.retrieveAllUsers(processAllCompletion(obtainUserCredentials)(doSomething));
}

Simply use the following code to execute:

iterateThroughUsers(function(tempCredentials){
    //tempCredentials will contain all credentials
});

There are potentially more efficient methods for managing the order of array values insertion in processAllCompletion. The function processAllCompletion operates by accepting a function that takes a step and executing it with a step function that will invoke another step function once all calls are completed. I have utilized currying in the functions for convenience.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue with generating WebGL shaders

As I embark on creating a basic application in WebGl and JavaScript following online tutorials, I encountered a peculiar issue while working on some fundamental shaders. The function responsible for generating the shader program presents itself as follows: ...

Leaving the "OK" button untouched in the sweet alert dialog

While implementing sweet alert in my project, I encountered an issue where the button text was not changing and the cancel button was missing on some pages. On certain pages, it only displayed OK. You can see a screenshot below for reference. https://i.s ...

Eliminating an element from an object containing nested arrays

Greetings, I am currently working with an object structured like the following: var obj= { _id: string; name: string; loc: [{ locname: string; locId: string; locadd: [{ st: string; zip: str ...

What is the method for changing a function's parameter type?

Currently, I am involved in a project that is based on classes and I have my primary class which I usually extend to other files because the implementation remains quite similar. Here's one of the functions in the BaseSummaryManager Page Object file: ...

Warning: React JS encountered errors with propType validation and uncontrolled input handling

I've been encountering challenges with React JS and trying to enhance the todoList after completing a crash course. Despite spending 8 hours troubleshooting, I'm still struggling. Hopefully, seeking assistance here will provide me with a fresh pe ...

Streaming videos on Xbox One is not supported while in developer mode

Currently, I am facing a challenge with my UWP app that runs a web application made for Xbox One. There is a bug that only appears after approximately 4 hours of streaming video content, causing the app to freeze unexpectedly on the machine. The issue lie ...

A guide on passing parameters from ui-sref in ui-router to the controller

Is there a way to pass and receive two parameters when transitioning to a different state using ui-sref in ui-router? For example, how can I transition to the home state with both foo and bar parameters like this: <a ui-sref="home({foo: 'fooV ...

Having trouble properly iterating through a Javascript JSON pull?

Here is the code that I am working with: var input=require('./task.json'); const _ = require(`underscore`); var dps = []; for(var element in input) { for (var i=0;i>dps.length;i++){ if(dps[i].Technician===input[element].Technician ...

Adjust the color and surface appearance using three.js

Struggling to modify the color and image of a material, I attempted to do so with the code below: material = new THREE.MeshPhongMaterial({map:THREE.ImageUtils.loadTexture('3d/caneca/e.jpg')}); Unfortunately, my attempts were unsuccessful. The o ...

The modal appears on the screen prior to the content being shown

While attempting to render a bootstrap modal with content from a REST call, I am encountering an issue where the modal appears before the content has finished populating. The modal is triggered by a button click event. If I click the button again after wa ...

Django plugin designed for showing a real-time feed of messages - powered by Dajax or Jquery?

Currently, I am attempting to set up a section in my Django application where updates or messages from the server can be displayed once specific tasks are done. I had initially looked into using a plugin that utilizes Dajax / Jquery for this feature, but ...

What is the equivalent of {...props} in React for destructuring props in Vue?

When working in React, I can destructure props with ease: function MyComponent() { const myProp = { cx: '50%', cy: '50%', r: '45%', 'stroke-width': '10%' } return ( <svg> ...

What is the best way to add pairs of divs to a single div?

Can you help me with this task? <div id="list"> <div class="item"></div> <div class="item"></div> <div class="item"></div> <div class="item"></div> <div class="item"></div& ...

Using regular expressions, you can eliminate a specific segment of a string and substitute

Provide a string in the following format: lastname/firstname/_/country/postalCode/_/regionId/city/addressFirst/addressSecond/_/phone I am creating a function that will extract the specified address parts and remove any extra parts while maintaining maxim ...

An issue occurred while testing with React-Native Testing Library/Jest, where the property 'TouchableOpacity' could not be read

I am currently in the process of conducting tests using jest and react-native testing. Unfortunately, I have encountered an issue where TouchableOpacity is not being recognized and causing errors. Card.test.js import Card from "../Card" import R ...

Having trouble transmitting parameters through ajax

I have been attempting to use ajax to send variables to a php page and then display them in a div, but unfortunately, it's not functioning as expected. Below is the HTML code: <div id="center"> <form> ...

What is the process for submitting a form from a different webpage?

I am working on a website with two pages. Each page has a header with navigation tabs to move between page 1 and page 2. When a tab is clicked, I want the selected page to re-POST to the server for a quick refresh before displaying. There is a form on e ...

Display the main body of the page while excluding the top and bottom sections

I have been experimenting with incorporating smooth transitions between the pages on my website using framer motion and react router. Below is a snippet of my code: <AnimatePresence initial={false} exitBeforeEnter> <Switch loc ...

While attempting to make my div responsive, it unexpectedly became stuck to the navbar

After successfully creating a website that works fine on desktop, I encountered an issue when viewing it on my phone. The main content section, which features jokes, gets scrolled up along with the navbar. I followed a tutorial on YouTube to make the navba ...