Anubha Anubha - 1 year ago 50
AngularJS Question

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 : - valid - invalid - valid - 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 ?

Answer Source

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

// 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();

      if (/(yahoo|gmail)/.test({
        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: '', name: 'Joe'},
    {email: '', name: 'Abc'},
    {email: '', name: 'XYZ'}, 
    {email: '', name: 'test'}

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

  for(var i=0; i < this.emails.length; i++) {

    this.isEmailValidList = emails.filter(function(e){ return e; });


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