简体   繁体   中英

What's the best way to loop through ajax calls

I'm working with Sharepoint and I have an app that creates lists in the site collection (not the app!). Creating the list isn't a problem. I wan't to create 15 columns and add them to the default view. I prepared a "config" with the information for the list and its fields.

var ChangeRequestLogListConfig = {
    listName: 'ChangeRequestLog',
    fields: [
        { 'FieldTypeKind': 8, 'Title': 'STC Relevant', 'InternalName': 'STCrelevant', 'StaticName': 'STCrelevant' },
        { 'FieldTypeKind': 3, 'Title': 'Description/Reason', 'InternalName': 'DescriptionReason', 'StaticName': 'DescriptionReason' },
        { 'FieldTypeKind': 6, 'Title': 'Source of Change', 'InternalName': 'SourceOfChange', 'StaticName': 'SourceOfChange', 'Choices': { 'results': ['customer', 'internal'] } },
        //12 more objects like the above
    ]
}

There we come to the problem: with the rest resources .../ViewFields/AddViewField and .../fields (with http post to create new ones) only support one parameter which means I have to do 2x15 ajax calls.

What's the proper approach to doing all these 30 operations and then do a final callback? Of course I know how to loop through the array of fields, I simply have no clue how to build that final callback the proper way.

Update / Follow-up

With the help of a few answers I managed to build the following code:

var spExecutor = new SP.RequestExecutor(_spPageContextInfo.siteAbsoluteUrl);
var requestUrl = _spPageContextInfo.siteAbsoluteUrl + "/_api/SP.AppContextSite(@target)/web/Lists/getbytitle('" + listConfig.listName + "')/fields?@target='" + hostWebUrl + "'";

//map field configs to promises
var promises = listConfig.fields.map(fieldConfig => {
    return spExecutor.executeAsync({
        url: requestUrl,
        method: "POST",
        body: JSON.stringify(fieldConfig),
        headers: {
            "accept": "application/json;odata=verbose",
            "content-type": "application/json; odata=verbose"
        },
        error: err => console.log(err)
    });
});

//Wait for all calls to be done
$.when.apply($, promises).then(callbackFn);

As you probably already saw, I'm not using $.ajax but the SP.RequestExecutor . The problem: This doesn't return promises the same way as jQuery's ajax. This results in timing problems (--> 15 Calls at once, browsers mostly only support 4 parallel calls). The code above works, if I set a breakpoint and wait 1-2 seconds between the calls it creates all fields as expected.

My follow-up question: How can I wait for the completion of the first call to init the second, then the third and so on? I'm not sure if I'm using the promises the right way.

Use .map to convert each field into a Promise :

var promises = ChangeRequestLogListConfig.fields.map(
    item => $.ajax( ... )
);

and then $.when to wait for them all to complete:

$.when.apply($, promises).then( ... );

Note that this will start as many AJAX requests in parallel as your browser will permit - typically four.

EDIT since you've said you actually want the AJAX queries to run in series (to avoid server 409 errors) my version of your addFieldsToList function would look like this:

function addFieldsToList(listConfig) {

    return listConfig.fields.reduce(function(promise, field) {
        return promise.then(executeRequest(field, listConfig.listName));
    }, Promise.resolve(null));

}

avoiding passing the callback, since the return value of this function will itself be a Promise that you can chain:

addFieldsToList(myConfig).then(callback);

Finally I ended up doing this:

//Promise returning request
function executeRequest(fieldConfig, listName) {
    return function () {
        return new Promise(function (resolve, reject) {
            var requestUrl = _spPageContextInfo.siteAbsoluteUrl + "/_api/SP.AppContextSite(@target)/web/Lists/getbytitle('" + listName + "')/fields?@target='" + riskapp.utils.getSpHostUrl() + "'";
            var spExecutor = new SP.RequestExecutor(_spPageContextInfo.siteAbsoluteUrl);
            spExecutor.executeAsync({
                url: requestUrl,
                method: "POST",
                body: JSON.stringify(fieldConfig),
                headers: {
                    "accept": "application/json;odata=verbose",
                    "content-type": "application/json; odata=verbose"
                },
                success: resolve,
                error: reject
            });
        });
    };
}

//Add fields to list according to config
function addFieldsToList(listConfig, callback) {

    //Prepare empty/resolved promise to iterate later
    var promise = Promise.resolve(null);

    //Loop through every field 
    $.each(listConfig.fields, function (i, field) {
        promise = promise.then(executeRequest(listConfig[field], listConfig.listName));
    });

    //execute callback when all fields are created
    promise.then(callback);
}

This executes all the calls in an order, not simultaneously.

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