简体   繁体   中英

$q promise with foreach

I am writing an angular service to work with SharePoint data and I have run into a problem. I have a function in my service that updates and single item and returns an $http promise which works fine. The problem is I am trying to write a function now that utilizes the first function to loop and update multiple items. I want it to return a single promise once all items have been updated and it should reject if any of the items being updated failed. Here is the function:

this.UpdateListItems = function (webUrl, listName, itemsJson) {
    if (numItems == -1) {
        numItems = itemsJson.length;
        c = 0;
        f = 0;
    }
    var promises = [];

    itemsJson.forEach(function (itemProps) {
        var itemPromise = this.UpdateListItem(webUrl, listName, itemProps.Id, itemProps)
            .then(function (response) {
                c++;
                if (c == numItems && f == 0) {
                    numItems = -1;
                    return itemsJson[listName];
                }
            }, function (error) {
                c++; f++;
                alert("ERROR!");//This gets called first alert below
                if (c == numItems) {
                    numItems = -1;
                    return $q.reject(error);
                }
            });
        promises.push(itemPromise.$promise)
    }, this);
    return $q.all(promises)
        .then(function (data) {
            alert("IN SUCCESS"); //This always gets called immediately after first item success instead of waiting for all items to finish
        }, function (error) {
            alert("IN ERROR"); //This never gets called
        });
};

The $q.all is returning immediately after the first item returns successfully instead of waiting for the rest of the async item calls. Any help is much appreciated, I am new to all this. Thanks!

EDIT: Adding UpdateListItem code as requested:

this.UpdateListItem = function (webUrl, listName, itemId, itemProperties) {
    if (typeof lists[listName] === 'undefined') {
        lists[listName] = [];
    }
    var post = angular.copy(itemProperties);
    DataUtilitySvc.ConvertDatesJson(post);
    return this.GetListItemById(webUrl, listName, itemId)
        .then(function (item) {
            return $http({
                url: item.__metadata.uri,
                method: 'POST',
                contentType: 'application/json',
                processData: false,
                headers: {
                    "Accept": "application/json;odata=verbose",
                    "X-HTTP-Method": "MERGE",
                    "If-Match": item.__metadata.etag
                },
                data: JSON.stringify(post),
                dataType: "json",
            }).then(function (response) {
                var temp = [];
                temp.push(itemProperties);
                DataUtilitySvc.MergeByProperty(lists[listName], temp, 'Id');
                return response;
            }, function (error) {
                return $q.reject(error);
            });
        }, function (error) {
            return $q.reject(error);
        });
};

Seems like this.UpdateListItem function already returned promise by having $promise object. That's why you were able to have .then (chain promise) function over it.

So basically you just need to push returned itemPromise object instead of having itemPromise.$promise inside promises array. Basically when you are doing $promise , it creates an array of [undefined, undefined, ...] and will resolve as soon as for loop completed.

Change to

promises.push(itemPromise)

from

promises.push(itemPromise.$promise)

Somewhat this question can relate to this answer

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM