Potential Asynchronous Invocation within a Condition

Currently, I am in the process of restructuring some older code that was not originally written by me. A specific issue has arisen related to loading asynchronous data. When a particular modal is first activated, a significant amount of data representing a form object is loaded. Subsequently, a function iterates over the form inputs and populates them as needed. This is a very basic representation:

component.inputs.forEach(function(input) {
    if (input.field == 'foo') {
        input.cols = 5;
        //more actions here
    }

    if (input.field == 'bar') {
        DataService.getBars().then(function(data){
            data.forEach(function(e){
                input.options.push(e.description);
            });
        };
    }

    if (input.field == 'baz') {
        input.pattern = /regex/;
        //more actions here
    }
});

return component;

An issue arises when the program encounters an async call to DataService within the 'bar' input section. The code proceeds to the final return statement before the Promise from DataService resolves. Consequently, upon the initial opening of the modal, the 'bar' input remains unfilled.

I am contemplating possible solutions such as making the code pause until the Promise from DataService completes or finding an alternative approach to handle situations where the function is mostly synchronous but requires an async operation in one instance. Could it be that by integrating an async call into this series of conditionals, I have inadvertently created a complication?

Answer №1

To optimize the process, one method is to generate a promise and connect it as an attribute to the returned object.

function fetchItem() {
    item.list.forEach(function(listItem) {
        //generate initial promise
        var $promise = $q.when(listItem);
        if (listItem.field == 'foo') {
            listItem.columns = 5;
            //etc.
        }
        if (listItem.field == 'bar') {
            //chain from initial promise
            $promise = $promise.then(function () {
                 //return promise for further chaining
                 return getBarPromise(listItem);
            });
        }
        //append promise to list item object
        listItem.$promise = $promise;
    });

    var promises = [];
    angular.forEach(items, function(item) {
        promises.push(item.$promise);
    });
    //compose final promise
    var $promise = $q.all(promises);

    //final chain 
    $promise = $promise.then( function() {
         //return component for further chaining
         return component;
    });
    //append promise to component  
    component.$promise = $promise;

    return component;
};

The resulting component entity will eventually be populated with the data retrieved from the service calls. Functions that require all service calls to finish can cascade from the linked $promise property.

$scope.component = fetchItem();

$scope.component.$promise.then( function (resolvedComponent) {
    //trigger modal display 
}).catch( function(errorResponse) {
    //record error response
});

Utilizing the then function of a promise generates a new related promise, making it simple to form a series of promises. By allowing a promise to resolve with another promise, which delays resolution, pauses can be introduced at various stages in the chain. This flexibility enables the implementation of robust APIs.1

Answer №2

If you wish to keep the current code structure intact and make it function, you will most likely have to implement the use of promises. Another option is to utilize JavaScript's map function. Please note that you will need to include $q wherever you intend to execute this function.

function getComponent() {
    var deferred = $q.defer(),
        deferred2 = $q.defer(),
        promises = component.inputs.map(function(input)) {
            if (input.field == 'foo') {
                input.cols = 5;
                deferred2.resolve();
            }
            else if (input.field == 'bar') {
                DataService.getBars().then(function(data) {
                    data.forEach(function(e){
                        input.options.push(e.description);
                    });
                    deferred2.resolve();
                }).catch(function(err)) {
                    deferred2.reject(err);
                });
            }
            else if (input.field == 'baz') {
                input.pattern = /regex/;
                deferred2.resolve();
            }

            return deferred2.promise;
        });

    $q.all(promises)
        .then(function() {
            deferred.resolve(component);
        }).catch(function(err) {
            deferred.reject(err);
        });

    return deferred.promise;
}

After each input in component.inputs has been processed correctly, the $q.all block will be triggered, allowing you to return your updated component object.

To assign your new component object, follow these steps:

getComponent().then(function(result)) {
        //Set component object here with result
        $scope.component = result;
    }).catch(function(err) {
        // Handle error here
    });

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

Chrome functions properly, while Firefox throws a TypeError stating that document.getElementById(...) is null

Despite my efforts to find a solution, I have yet to resolve the problem I'm facing with Firefox and IE. The error message TypeError: document.getElementById(...) is null only appears in these browsers, while Chrome and Safari work without issue. You ...

Zero-length in Nightmare.js screenshot buffer: an eerie sight

I'm currently working on a nightmare.js script that aims to capture screenshots of multiple elements on a given web page. The initial element is successfully captured, but any subsequent elements below the visible viewport are being captured with a l ...

What is the best way to accurately measure the distance between two points on a 360 image using A-Frame technology?

Is there a way to accurately measure the distance between two points in a 360 picture of an interior using the a-frame.io framework? We attempted converting the unit system of a-frame to centimeters and used two points with known dimensions as a reference ...

Executing API call utilizing the Request module within a node.js application

In my node.js app, I have a request call that looks like this: request({ url:chanURL, qs:chanProperties}, function(err, response, body) { if(err) { console.log(err); return; } body = JSON.parse(body); (function (body) { Objec ...

Sending JSON-encoded data using HTML5 Server-Sent Events (SSE) is a

I have a script that triggers an SSE event to fetch json encoded data from online.php. After some research, I discovered methods for sending JSON data via SSE by adding line breaks. My question is how to send JSON through SSE when the JSON array is genera ...

How to use JavaScript to hide an element (field) in XPages

In XPages, I understand that only editable fields are able to retrieve values from context. My question is: how can I hide a field using CSS or JavaScript in XPages while still being able to get its value from the context? Many thanks ...

What issues are hindering the successful export of my Vue component packaged with npm?

I created a small local npm package called fomantic-ui-vue with the following main js file: import Vue from 'vue' // Import vue component import button from './elements/button/button.vue' import buttonGroup from './elements/butt ...

Discover and capture zip codes using javascript

Looking to extract zip codes from strings like "HONOLULU HI 96814-2317 USA" and "HONOLULU HI 96814 USA" using JavaScript. Any suggestions on how to achieve this? ...

Enriching an array with values using Mongoose

Is there a way to assign a User as an admin for the School? School Schema: const School = new Schema({ name: { type: String, required: true }, grades_primary: [{ type: Schema.Types.ObjectId, ref: 'Grade' }], grades_secondary: [{ type ...

Tips for creating a personalized callback within a user function using JavaScript

Utilizing callbacks is a common practice when working with third-party libraries like jQuery. However, I have encountered a situation where I need to implement my own callback function. Consider the following snippet from my current code: // Get All Rates ...

What's causing these line break errors to occur for me?

When working in VSC, I have configured ESlint with the Airbnb settings. To test it out, I ran this code: const a = 'a'; const b = `${a}Hi`; const d = function () { return b; }; d(); The issue arises with the linebreaks: Expected linebreaks t ...

Generating an ordered array that begins at a specified value and has a specific number of elements

Searching for a stylish solution (without using a for loop) to generate a sequential array in Javascript that begins with a specific number and contains a set number of elements. For instance: Commencing with 2017 and including 4 items would appear as fol ...

Inspect the table and format the tr content in bold depending on the values found in two corresponding columns within the same table

Looking to create a responsive table where row content becomes bold if it matches the current date. Hiding the first-child th & td as they are only needed for a specific function. Comparing values in <td data-label="TodaysDate">06-05-2020</t ...

Executing a JavaScript onclick event following the submission of a form

Within my HTML form, I have a radio button that triggers a JavaScript onclick function to disable certain text fields. However, after submitting the form, the disabled fields revert back to normal textboxes. To address this issue, I am looking for a way t ...

What is the correct way to establish an array variable containing objects?

What is the correct way to declare an object within an array variable? I encountered the following error message: "TypeError: Cannot set property 'name' of undefined" Here is the code snippet in question: let data = [] data[0].name = "john" ...

When working with jQuery, I encountered the error message "is not a function" because I mistakenly tried to use a function more than

While working on a pager, I encountered an issue with a function that is initially invoked when the document loads. However, when attempting to use it a second time, an error "is not a function" occurs. I am curious about the reason behind this phenomenon. ...

Execute JavaScript code via URL injection

One interesting aspect of the HTML is that it has a feature where it opens a webpage. The specific webpage it opens is determined by the URL, for example: https://mywebsite.com/index.html&audio=disabled In addition to this, there is a useful JavaScri ...

Experimenting with broadcasting functionality in AngularJS and Testacular

I am currently utilizing the angular-http-auth plugin which catches 401 responses. Whenever there is a 401 response, this plugin triggers event:auth-loginRequired that can be caught using $on(). But how can I test this functionality? beforeEach(inject(f ...

Guide to utilizing various Nodelists using the forEach function

I'm currently developing an online store project that includes a shopping cart feature. My goal is to send a POST request to the server with the items selected by the user in the cart. I have initialized an empty array and I have 3 Nodelists containin ...

Selecting an option from the drop-down list will change a text box to read

My current challenge involves making certain text boxes read-only based on the injury selected from a drop-down list. For example, if "thigh," "arm," "head," "heart," or "fingers" are chosen, specific text boxes will be restricted for input. For instance, ...