0

Broken promises, thought I had it figured out, still trying to get reliable results. Console log shows hit and miss results. Below is the rundown of what I'm trying to achieve. Is there a simpler way to do this, I've been struggling with this for a week now just to update person fields, its ridiculous.

  • I have 2 multiperson people pickers in SharePoint.
  • MS gives you every user detail EXCEPT the user id which is needed to update via ajax
  • I can get the emails from the people pickers into arrays
  • My promises setup is hit and miss retrieving id's
  • When it works it outputs a string array that doesn't work with updating field

    eg: 1,2,3,4 instead of [1,2,3,4]

function test1a() {

    fEmailArray = [];
    tEmailArray = [];
    fIdArray = [];
    tIdArray = [];

    var FCon = $("div[title='From Contacts'] > input").val();
    var Fjson = JSON.parse(FCon);
    for (var i = 0; i < Fjson.length; i++) {
        fEmailArray.push(Fjson[i].EntityData.Email);
    }

    var TCon = $("div[title='To Contacts'] > input").val();
    var Tjson = JSON.parse(TCon);
    for (var i = 0; i < Tjson.length; i++) {
        tEmailArray.push(Tjson[i].EntityData.Email);
    }

    let promiseF = fEmailArray.map(fEmail => test2(fEmail));
    let promiseT = tEmailArray.map(tEmail => test2(tEmail));

    return Promise.all(promiseF)
        .then(function (data) { fIdArray = data; })
        .then(Promise.all(promiseT)
        .then(function (data) { tIdArray = data; }));            
}

function test2(xEmail) {

    let myPromise = new Promise((resolve, reject) => {
        $.ajax({ // get the Ids of users
            url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/SiteUsers?$select=Id&$filter=Email eq '" + xEmail + "'",
            type: "GET", headers: { "Accept": "application/json;odata=verbose", },
            success: function (data) {
                var xuserid = data.d.results[0].Id;
                resolve(xuserid);
            },
            error: function (error) {
                alert(JSON.stringify(error));
                reject();
            }
        });
    });
    return myPromise;
} // end test2
  • A big problem is the array output is string, instead of numeric ids. – BDC604 May 29 at 21:07
0

The best practice here would be to use JSOM, load all the queries that you need and then execute your function once it's done, so then you don't need to worry about timing.

Below is an example:

var currentUser;
function init(){
 this.clientContext = new SP.ClientContext.get_current();
 this.oWeb = clientContext.get_web();
 currentUsers = this.oWeb.get_currentUser();
 this.clientContext.load(currentUsers); /* you do it for all 
 the users you need to load */
 this.clientContext.executeQueryAsync(
 function(){
   /*Here you can access your whole query's result at once.
   use currentUsers.getEnumerator(); to convert the result to a 
   list.*/
 })
}
0

Not sure about it returning strings... I did a bunch of playing around with this, and all I ever got was integers.

Now, one thing you could do, if you wanted to keep your ajax call wrapped in a Promise as you have it now, is to combine all the requests/promises into one big array so that you have only one Promise.all() and are not chaining those:

let promiseF = fEmailArray.map(fEmail => test2(fEmail));
let promiseT = tEmailArray.map(tEmail => test2(tEmail));

let fBatchLength = promiseF.length;

// expand both promise arrays into one array so you only have
// one Promise.all() - the results will preserve the order!
return Promise.all([...promiseF, ...promiseT])
    .then(function (data) {

        // you know your "f"s were first, so pull them out using splice
        fIdArray = data.splice(0, fBatchLength);

        // splice actually changes the original array, so
        // there's nothing left in data except the "t"s
        tIdArray = data;

        // you return your Promise.all, so is it fair to say
        // that the code that called function test1a() is expecting
        // a result?  if so you need to return something out of the then()
        return {
            fIDs: fIdArray,
            tIDs: tIdArray
        };
    });

However, $.ajax() returns a deferred object, which is kind of like a promise, so you could unwrap your ajax call from the other promises and just return that, and deal with digging the IDs out of the results in your main function. So test2 would turn into:

function test2(xEmail) {
    return $.ajax({ // get the Ids of users
        url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/SiteUsers?$select=Id&$filter=Email eq '" + xEmail + "'",
        type: "GET",
        headers: { "Accept": "application/json;odata=verbose", },
    });
}

and in the main function you could do:

let promiseF = fEmailArray.map(fEmail => test2(fEmail));
let promiseT = tEmailArray.map(tEmail => test2(tEmail));

let fBatchLength = promiseF.length;

// expand both promise arrays into one array so you only have
// one Promise.all() - the results will preserve the order!
return Promise.all([...promiseF, ...promiseT])
    .then(function (data) {

        // deal with the ajax responses
        var allIDs = data.map(function(response) {
            return response.d.results[0].Id;
        });

        // you know your "f"s were first, so pull them out using splice
        fIdArray = allIDs.splice(0, fBatchLength);

        // splice actually changes the original array, so
        // there's nothing left in allIDs except the "t"s
        tIdArray = allIDs;

        // you return your Promise.all, so is it fair to say
        // that the code that called function test1a() is expecting
        // a result?  if so you need to return something out of the then()
        return {
            fIDs: fIdArray,
            tIDs: tIdArray
        };
    });

I think either way the main thing is to get away from the chained Promise.alls.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.