Вопрос

I have two $http posts that each post arrays of objects in a loop. The problem is that the second $http post is reliant on the first one completing. Is there a way to make them not async calls? I tried to used deferred but something is wrong in it as it is not working. It still fires group saving while tag saving is going on.

Angular:

var deferred = $q.defer();
    var all = $q.all(deferred.promise);

    for (var property in data.tagAdded) {
        if (data.tagAdded.hasOwnProperty(property)) {
            $http({
                method: "POST",
                url: '/api/projects/' + data.Project.Id + '/tags',
                data: ({ Name: data.tagAdded[property].tag.Name })
            }).success(function (response) {
                deferred.resolve(response);
                data.tagAdded[property].tag.Id = response.Data[0].Id;
                data.tagAdded[property].tag.ProjectId = response.Data[0].ProjectId;
            }).error(function (response) {
                tagError = true;
                $.jGrowl("Error saving new tags. Contact support.", { header: 'Error' });
            });
        }
    }

    deferred.promise.then(function() {
        console.log(data);
    });

    all.then(function() {
        groups.forEach(function(group) {
            $http({
                headers: { 'Content-Type': 'application/json; charset=utf-8' },
                method: "POST",
                url: '/api/projects/' + data.Project.Id + '/recruiting-groups',
                data: angular.toJson(group, false)
            }).success(function(response) {

            }).error(function(response) {
                recError = true;
                $.jGrowl("Error saving recruiting group. Contact support.", { header: 'Error' });
            });
        });
    });
Это было полезно?

Решение

Going without promises is totally not what you want to do here. In fact, this is exactly the kind of situation where promises shine the most! Basically, you weren't using $q.all properly. You can just pass it a list of promises, and it will be resolved when they are all resolved. If any one of them fails, it will yield a rejected promise with the same rejection as the first one that failed. You can of course swallow that rejection via a .catch invocation that returns anything other than a $q.reject value.

I reimplemented what you had using promises. Both .success and .error are fairly limited, so I used the traditional .then and .catch methods here.

/**
 * Here we define a function that takes a property, makes a POST request to
 * create a tag for it, and then appends the promise for the request to a list
 * called tagRequests.
 */
var tagRequests = [];
var createTag = function(property) {
  tagRequests.push($http({
    method: "POST",
    url: '/api/projects/' + data.Project.Id + '/tags',
    data: ({ Name: data.tagAdded[property].tag.Name })
  }).then(function(response) {
    var responseData = response.data;
    data.tagAdded[property].tag.Id = responseData.Data[0].Id;
    data.tagAdded[property].tag.ProjectId = responseData.Data[0].ProjectId;
    return responseData;
  }).catch(function (err) {
    var errorMsg = "Error saving new tags. Contact support.";
    $.jGrowl(errorMsg, { header: 'Error' });
    // If we don't want the collective promise to fail on the error of any given
    // tag creation request, the next line should be removed.
    return $q.reject(errorMsg);
  }));
};

/**
 * We then iterate over each key in the data.tagAdded object and invoke the
 * createTag function.
 */
for (var property in data.tagAdded) {
  if (Object.prototype.hasOwnProperty.call(data.tagAdded, property)) {
    createTag(property);
  }
}

/**
 * Once all tag requests succeed, we then map over the list of groups and
 * transform them into promises of the request being made. This ultimately
 * returns a promise that is resolved when all group POST requests succeed.
 */
$q.all(tagRequests)
.then(function(tagsCreated) {
  return $q.all(groups.map(function(group) {
    return $http({
      headers: { 'Content-Type': 'application/json; charset=utf-8' },
      method: "POST",
      url: '/api/projects/' + data.Project.Id + '/recruiting-groups',
      data: angular.toJson(group, false)
    }).then(function(response) {
      return response.data;
    })
    .catch(function(err) {
      var errorMsg = "Error saving recruiting group. Contact support.";
      $.jGrowl(errorMsg, { header: 'Error' });
      // If we want this collective promise to not fail when any one promise is
      // rejected, the next line should be removed.
      return $q.reject(errorMsg);
    });
  }));
});

I highly suggest brushing up on Promises in general, and then taking another look at the $q documentation. I've also written this blog post on the way promises work in Angular and how they differ from most promise implementations.

Другие советы

This is exactly what promises do. Angular uses Kris Kowal's Q Library for promises. Check out the Angular docs page, has a perfect example of the promise pattern. Basically you would do the first call, then once it's promise returns success, make the second call.

I had to alter my code a bit, and get some coworker team work. I went completely without promises.

I use a counter to track when counter = 0 by adding to the counter then upon success/fail i decrement from the counter taking it backwards for each completed transaction. When counter is at 0 I am done then call my next bit $http post. Angular Deferred:

var counter = 0;
    var tags = [];
    for (var property in data.tagAdded) {
        if (data.tagAdded.hasOwnProperty(property)) {
            tags.push({ Name: property });
        }
    }
    if (tags.length == 0) {
        groupInsert();
    } else {
        tags.forEach(function (tag) {
            counter ++;
            $http({
                method: "POST",
                url: '/api/projects/' + data.Project.Id + '/tags',
                data: ({ Name: tag.Name })
            }).success(function (response) {
                console.log(response)
                counter--;
                data.tagAdded[property].tag.Id = response.Data[0].Id;
                data.tagAdded[property].tag.ProjectId = response.Data[0].ProjectId;
                if (counter == 0) {
                    groupInsert();
                }
            }).error(function (response) {
                counter--;
                tagError = true;
                $.jGrowl("Error saving new tags. Contact support.", { header: 'Error' });
                if (counter == 0) {
                    groupInsert();
                }
            });
        });
    }

    function groupInsert() {
        groups.forEach(function (group) {
            console.log(group)
            $http({
                headers: { 'Content-Type': 'application/json; charset=utf-8' },
                method: "POST",
                url: '/api/projects/' + data.Project.Id + '/recruiting-groups',
                data: angular.toJson(group, false)
            }).success(function (response) {

            }).error(function (response) {
                recError = true;
                $.jGrowl("Error saving recruiting group. Contact support.", { header: 'Error' });
            });
        });

    }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top