1

I'm just starting: (1)to code (2)for SP (3)with javascript(3) ... and my first choice was a JSOM. Once i've been trying hard to get a value from executeQueryAsync call and set it to local/global variable with promises, deferreds and so on, but without any success. Among all problems there was also my failures with making code work in order i need to get values. Dissynchronization in other words. So i switched to REST and now my average code looks like:

    var listTitle; 
    var itemName;
    var itemId;
    var fileName; 

    $.when(getListName()).then(getItemName()).then(other_function())......done();

    function getListName() { 
    //local variables here 
    $.ajax({
        url: siteUrl + '/_api/web/lists(guid' + listId + ')',
        async: false,
        type: 'GET',
        headers: { 'accept': 'application/json;odata=verbose' },
        success: function (responseData) {
            var a = somehow_get_listTitle_from_response(); 
            listTitle = a;

        },
        error: errorHandler
    });
    function getItemName() { 
    // almost the same code with async: false
    itemName = somehow_get_itemName_from_response()

}
   // other funcntions like two above;
}

Well, for now it works, but i'm very curious, is that continuous using of async:false a..."good" style to write SP web parts, custom application pages, custom action and so on? Or smart guys do this stuff somehow else in common?

2 Answers 2

2

I do this pretty frequently. Here is a pattern I tend to follow.

function getListTitle(guidList) {
  var thisContext = {
    spContext : SP.ClientContext.get_current(),
    promise: new $.Deferred()
  };
  thisContext.spWeb = thisContext.spContext.get_web();
  thisContext.spList = thisContext.spWeb.get_lists().getById(guidList.toString());
  thisContext.load(thisContext.spList);
  thisContext.executeQueryAsync(
    Function.createDelegate(thisContext, getListTitle_done),
    Function.createDelegate(thisContext, getListTitle_fail)
  );
  return thisContext.promise;
}
function getListTitle_done() {
  this.promise.resolve(this.spList.get_title());
}
function getListTitle_fail(sender, args) {
  this.promise.reject(args.get_message());
}
getListTitle("2c5e4c06-0704-485f-827b-3815ff9a870f").then(
  function (nameList) {
    console.log("The name of the list is '" + nameList + "'.");
  }
).fail(
  function (msgError) {
    console.error(msgError);
  }
);
2

No async:false is not a good pattern. Browsers like chrome will throw a warning see this documentation

Synchronous XMLHttpRequest outside of workers is in the process of being removed from the web platform as it has detrimental effects to the end user’s experience. (This is a long process that takes many years.) Developers must not pass false for the async argument when current global object is a Window object. User agents are strongly encouraged to warn about such usage in developer tools and may experiment with throwing an "InvalidAccessError" DOMException when it occurs.

That is what the jQuery Deferred or Promise is for. Without know all the details (which would be better settled by posting specific follow up questions) you can use the chaining .then() to not execute the next part of your code until the previous part is done.

Here is how I would start to rethink whatever it is you're trying to do.

function myProcess(){

    return getListName()
                .then(getItemName)
                .then(otherFunction)
                .always(function(){
                    // do anything here
                })
                .fail(function(){
                    console.log("Error from myProcess()");
                });
}

function makeRestCall(obj){
    return $.ajax(obj).fail(function(){console.log("Error from makeRestCall()");});
}

function getListName(){
    return makeRestCall({
        url:"/api...",
        type:'GET',
        headers:headerObj
    });
}

function getItemName(data){
    // data is the promise/resolved promise returned by what is in the chain previous
    var listName = data.extractListNameFrom;

    return makeRestCall({
        // next object
    });
}

/// your main flow of code elsewhere

function mainFlow(){
    // do things
    // do things
    // time to do my rest calls

    myProcess()
        .done(function(){
            console.log("My process is done!");
        });

    // do other things that don't rely on myProcess being done
}

The key is that at each async stage you need to return a thenable value. In this case I've pulled out the ajax part and you'll notice that function returns the ajax call. Then each subsequent call returns the makeRestCall() function.

In this example I have getListName() return its value directly to the next then() in the change and it returns a jQuery Deferred item. But you could also structure it like this:

function getListName(){
    return makeRestCall({
            url:"/api...",
            type:'GET',
            headers:headerObj
    })
    .then(function(data){
        var listName = data.extracListNameFrom;

        return $.when(listName);
    });
}

In that version you would be extracting the list name and returning it to the next function in the chain. Wrapping it in the when() keeps it thenable. Never just return a regular data value or it will break the chain.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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