Anubha
Anubha

Reputation: 1415

How to use asynchronous calls in a loop in angular?

Each email in a list is to be sent to server and response to be be got from server that if it is a valid email.

So after all emails are checked the array should have :

[email protected] - valid
[email protected] - invalid
[email protected] - valid
[email protected] - invalid

The code to send the emails to server in a loop is like :

for(var i=0; i < isEmailValidList.length; i++) {
  var isEmailValid = User.validateEmail({email : isEmailValidList[i].email}, function(){
      isEmailValidList[i].isValid = isEmailValid.value;
  });
}

But the problem is that the calls are asynchronous, so for say i=0, the control will not go inside the function when i is 0. So when it does go inside the function value of i can be anything, mostly it is greater than length of array, so isEmailValidList[i] is undefined. If the call were synchronous then it would have waited for response and i would not have been incremented, but this is not the case.

So, how do I get the correct isValid response for its corresponding email ?

Upvotes: 2

Views: 9071

Answers (2)

Engineer
Engineer

Reputation: 48793

You can use closure function:

for (var i=0; i < isEmailValidList.length; i++) {
  var isEmailValid = User.validateEmail({
     email : isEmailValidList[i].email
  }, (function(i) {
     return function() {
         isEmailValidList[i].isValid = isEmailValid.value;
     };
  })(i));
}

Upvotes: 3

pocesar
pocesar

Reputation: 7050

Use promises. Angular can work with promises without any "special intervention", just like assigning a value to a scope variable, see the plnkr. Promises are the "base" to tame asynchronous programming to work like synchronous programming (while we don't have javascript generators in browsers) and is encouraged by Angular team because it's highly testable and maintainable

http://plnkr.co/edit/8BBS2a1kC24BHBWRYp9W?p=preview

// trying to emulate your service here

var app = angular.module('app', []);

app.factory('User', function($q, $timeout){
  User = {};

  User.validateEmail = function(email){
    var d = $q.defer();

    $timeout(function(){
      if (/(yahoo|gmail)/.test(email.email)){
        d.resolve(email); // return the original object, so you can access it's other properties, you could also modify the "email" object to have isValid = true, then resolve it
      } else {
        d.resolve(); // resolve it with an empty result
      }
    }, 1000, false);

    return d.promise;
  };

  return User;
  });

app.controller('MainCtrl', function(User, $q){
  this.emails = [
    {email: '[email protected]', name: 'Joe'},
    {email: '[email protected]', name: 'Abc'},
    {email: '[email protected]', name: 'XYZ'}, 
    {email: '[email protected]', name: 'test'}
  ];

  this.isEmailValidList = [];
  var promises = [];

  for(var i=0; i < this.emails.length; i++) {
    promises.push(
      User.validateEmail(this.emails[i]) 
    );
  }

  $q.all(promises).then(function(emails){ 
    this.isEmailValidList = emails.filter(function(e){ return e; });
  }.bind(this));

});

Notes: The $timeout is to emulate an asynchronous task, like a database call, etc. You could pass the entire emails array to the validation service then return the array, instead of creating an intermediate array of promises. With angular, you may assign a scope variable to a promise, and you can use it on ng-repeat without any changes to the code

Upvotes: 5

Related Questions