Adding the output from a promise to an array

I am attempting to clear out and then refill an array with values retrieved from a promise. However, I've noticed that the values don't always get added back in the same order.

$scope.$watch ('timeRange', function (newValue, oldValue, scope){
     $scope.chartdata = []
     //If a filter has changed, redraw all charts
     if (newValue !== oldValue)
     {
        for(var i = 0; i< $scope.charts.length; i++){
           $scope.draw($scope.charts[i]).then(function(value){
               $scope.chartdata.push(value);
           });
        }
     }
}, true);

This content is being rendered using an ng-repeat directive.

Answer №1

When working with asynchronous tasks, the order of resolution may not always be guaranteed. To ensure consistency, consider using index i instead of push.

$scope.$watch ('timeRange', function (newValue, oldValue, scope){
     $scope.chartdata = []
     //If a filter has changed, redraw all charts
     if (newValue !== oldValue)
     {
        for(var i = 0; i< $scope.charts.length; i++){
           $scope.draw($scope.charts[i]).then(function(i) { // create scope to capture i
               return function(value) { $scope.chartdata[i] = value; };
           }(i));
        }
     }
}, true);

UPD Added an example to demonstrate how scopes work.

var arr = [1, 2, 3];

for (var i = 0; i < arr.length; i++) {
  setTimeout(function(i) {
    return function() {
      console.log(`Let's teach @georgeawg scopes ${i}`)
    }
  }(i), i * 1000)
}

You can also use forEach as shown below:

$scope.$watch ('timeRange', function (newValue, oldValue, scope){
     $scope.chartdata = []
     //If a filter has changed, redraw all charts
     if (newValue !== oldValue)
     {
        $scope.charts.forEach(function(chart, i) {
          $scope.draw(chart).then(function(value) {
             $scope.chartdata[i] = value;
          })
        })
     }
}, true);

Alternatively, you can add all at once using Promise.all or its AngularJS equivalent $q.all.

$scope.$watch ('timeRange', function (newValue, oldValue, scope){
     $scope.chartdata = []
     //If a filter has changed, redraw all charts
     if (newValue !== oldValue)
     {
        $q.all($scope.charts.map(function(chart) {
          return $scope.draw(chart)
        }).then(function(chartdata) {
          $scope.chartdata = chartdata;
        })
     }
}, true);

Answer №2

Code Snippet with Error

function generateRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function asynchronousFunction(index) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(index), generateRandomInt(0, 3000));
  });
}

const resultList = [];

for (var i = 0; i < 5; i++) {
  asynchronousFunction(i)
    .then((value) => {
      resultList.push(value);
    });
}

setTimeout(() => {
  console.log(resultList);
}, 4000);


Corrected Code Snippet

To address the issue, we can encapsulate the data position within an IIFE function.

function generateRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function asynchronousFunction(index) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(index), generateRandomInt(0, 3000));
  });
}

const resultList = [];

for (var i = 0; i < 5; i++) {
  (function(j) {
    asynchronousFunction(i)
      .then((value) => {
        resultList[j] = value;
      });
  })(i);
}

setTimeout(() => {
  console.log(resultList);
}, 4000);

Answer №3

To achieve this in a neat way, you can utilize .map along with Promise.all (or $q.all() in AngularJS). This method not only preserves the order of items but also enables you to track when the entire array has been filled.

Here's an example based on Gregory's code snippet:

(Note: The following code snippet uses setTimeout, new Promise, and Promise.all for demonstration purposes in a runnable Stack Snippet. In real AngularJS code, you would typically omit setTimeout and new Promise, opting for $q.all instead of Promise.all, as demonstrated at the end)

function randInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function delay(ms) {
  return new Promise(function (resolve) { setTimeout(resolve, ms); });  
}

function asyncFunc(value) {
  return delay(randInt(0, 3000))
  .then(() => { 
      console.log(value, 'finished');
      return value * 2 + 1;
  });
}

const origArray = [0, 1, 2, 3, 4];

Promise.all(origArray.map(asyncFunc))
    .then(resultArray => {
      console.log(resultArray);
    });

Adapting this to your specific code scenario, it would look like:

$scope.$watch('timeRange', function(newValue, oldValue, scope) {
     $scope.chartdata = [];
     // If a filter has changed, redraw all charts
     if (newValue !== oldValue) {
        $q.all($scope.charts.map($scope.draw))
            .then(resultArray => {
                $scope.charts = resultArray;
            });
     }
}, true);

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

using jquery to retrieve the current time and compare it

This is the code I currently have: var currentTime = new Date() var month = currentTime.getMonth() + 1 var day = currentTime.getDate() var year = currentTime.getFullYear() var hours = currentTime.getHours() var minutes = currentTime.getMinutes() aler ...

Incorporate content from a single HTML file into a different one

Hello, I am working with two HTML documents - let's call them index.html and index2.html for this example. I am looking to embed all the code within the body tag of index2.html into a section within index.html. Is there a way to create this connectio ...

The Reactjs infinite scroll feature continuously displays fresh data

Every time I scroll, my data is always rendering on page 2 and page 1 disappears, and the items.concat function is not working properly. My fetch data code looks like this: const FetchUrl = async (p) =>{ const Url = await fetch(`api-link=${p}`); ...

As two divs glide across the screen, the top div remains hidden from view

To enhance our user experience, we are tasked with creating a captivating screen saver featuring 3 images. The bottom image will remain fixed in its position. The middle image should move across the screen. Lastly, the top image will move at a faster ...

Validating a particular value using Regex in React Formik

First, I need to ensure that the field is validated for any characters not included in this set: /[ùûüÿ€’“”«»–àâæçéèêëïîôœ]/. If a user enters a character outside of this set, I want Yup to trigger an error message. Secondly, I ...

What is the best way to create a close button using JavaScript?

I just started learning JavaScript and I want to figure out how to make the close button close the div .popup1 in the following code: https://jsfiddle.net/74hmx0vb/1 <div class='popup1' id="popup1"> <div class="containe ...

Personalizing MaterialUI's autocomplete functionality

Trying to implement the material-UI Autocomplete component in my react app and looking for a way to display a div when hovering over an option from the list, similar to what is shown in this reference image. View example If anyone has any tips or suggest ...

Navigate to a New Page post Dropdown Selection

Currently working on developing a website using Github Pages where I need users to be directed to a specific page after choosing the final option. I am extracting data from a JSON file. I attempted to include an anchor tag in the JSON but it does not red ...

Button to close Jquery Dialog

I've set up a custom alert UI using jQuery UI, but I'm having trouble getting the close button to work properly. Here's my code snippet where I'm trying to override the default alert() function with jQuery UI dialog as described in this ...

The submission of the form proceeds even with the warning displayed

I am facing an issue with a form that consists of one text field and a submit button. The user is required to input a 3-digit number, and if the number is greater than 200, a warning should be displayed. If the user clicks OK, the form should be submitted. ...

Uh oh! React encountered a cross-origin error and is unable to access the real error object during development

I've encountered an issue while attempting to extract and display data from the student_learn field in firestore. Despite my efforts to map it to jsx components, I keep receiving a cors-origin error. Below is a screenshot of the collection I'm tr ...

Substitute <br /> tags with a line separator within a textarea using JavaScript

Anyone have a quick fix for this? I've been searching online for a solution, but no luck so far. Already tried checking out: this, this Currently dealing with a textarea that receives data from AJAX call and it ends up looking like this: Hello& ...

AngularJS: The art of object pushing

I have a small application where I need to read data from a JSON file, display it, and allow users to add records to it. Specifically, I have an array called condition within the patient object, and I want to insert a new item into this array based on user ...

Can you provide the regular expression that will successfully match this specific string of text?

Can you solve this fruit riddle? apple is 2kg apple banana mango is 2kg apple apple apple is 6kg banana banana banana is 6kg If the fruits are limited to "apple", "banana", and "mango", how can we write a regex that extracts the names of ...

Do we need to use the render method in ReactJs?

I'm curious about how ReactJs uses its Render() functionality. Let's say we have some HTML code within index.html: <section id="Hello"> <h1>Hello world</h1> <p>Something something Darkside</p> </section&g ...

Is it possible in Angular to generate a module and component without including a CSS file in a single command?

Is it possible to generate a Module linked to a component without automatically creating a css file? For example, the default method I've been using involves: ng generate module name / ng generate component name This results in the typical componen ...

Transform the appearance of several elements using querySelectorAll

When targeting class names in an HTML page with a checkbox using querySelectorAll, keep in mind that getElementByID only works with the first element. QuerySelectorAll displays a nodeList which might require some corrections - how can this be achieved? le ...

The attachment with Ajax is not displaying any file data

I am encountering an issue while attempting to send an email to myself along with an attachment. Instead of using the standard php mail function, I have opted for PHPMailer due to its convenience. The data is being processed via an Ajax call after extensiv ...

Creating pagination in Vue using an array of objects

I'm new to Vue and arrays and I need help paginating my json array so that only 10 items are displayed per page. Currently, all the items in the array are being shown in the <tr> body. I've tried different methods without success. Can someo ...

divide a JSON list

Within this JSON array, there is a plethora of person data, each with their own specified type such as "hr" or "accounting". The structure of the JSON array is depicted below: { "0": { "id": "2", "name": "blabla", "type": "hr" ...