6

I'm about to query multiple lists using JavaScript (JSOM, SharePoint 2010). It's working when querying one list in a time, but that's annoying when I'm having lots of lists. Working example:

    function createLists() {
      getListData1();
      getListData2();
    }   

    function getListData1() {
      var context = new SP.ClientContext(url);
      var camlQuery = new SP.CamlQuery();
      [...]
      context.load(listItems);
      context.executeQueryAsync(
             Function.createDelegate(this, this.onQuerySuccess1),
             Function.createDelegate(this, this.onQueryFailed)
        );
    }

    function getListData2() {
      [...]
      context.executeQueryAsync(
             Function.createDelegate(this, this.onQuerySuccess2),
             Function.createDelegate(this, this.onQueryFailed)
        );
    }

But what I want to do is to reuse the function extractData, like this:

    function createLists() {
      getListData(url, List1);
      getListData(url, List2);
    }  

    function getListData(siteUrl, listName) {
      var context = new SP.ClientContext(siteUrl);
      var camlQuery = new SP.CamlQuery();
      var list = context.get_web().get_lists().getByTitle(listName);
      [...]
      context.load(listItems);
      context.executeQueryAsync(
             Function.createDelegate(this, this.onQuerySuccess),
             Function.createDelegate(this, this.onQueryFailed)
        );
    }       

Unfortunately that seems to create some conflict, probably has to do with the asynchronous function? Is it possible to reuse an asynchronous call like this or do I need to create individual functions for each and every query?

4
  • My first thought is to try and use Promises to make the individual queries to the lists, because there is a Promise.all(iterable) function that will wait until all the promises (i.e. async operations) that have been passed into it are complete before proceeding. I am very far from knowledgeable on how to implement it though, so I will leave this as a comment instead of an answer. Dec 15, 2015 at 16:12
  • I don't have 2010 but conceptually this should work. If you delete 2 individual items from a view SP does the same. (yes you would have to click very fast) Converting it from Functional to OOP only changes the syntax, not the concept/context Dec 15, 2015 at 16:20
  • Thanks! :) I'll try that and will get back to this thread once it's done.
    – kolback
    Dec 16, 2015 at 12:13
  • maybe I'm confused, but why aren't you loading both sets of list items then making a single call to executeQueryAsync? This is one of the most obvious strengths of JSOM.
    – John-M
    Jan 7, 2016 at 5:10

3 Answers 3

5

The OOP suggestion solves your context problem but it is kinda like saying:

We have a flat tire, lets change cars!

Your issue is with the this context you give to your processing function, so lets fix that tire.

Instead of passing it the whole truck you only want to pass the payload:

with: processItems.bind(request)

function getItems(listName,siteUrl){
    var request={};
    request.listName=listName;
    request.siteUrl=siteUrl || _spPageContextInfo.siteAbsoluteUrl;
    var ctx = new SP.ClientContext(request.siteUrl);
    var list = ctx.get_web().get_lists().getByTitle(request.listName);
    request.query=SP.CamlQuery.createAllItemsQuery();
    request.itemCollection=list.getItems(request.query);
    ctx.load(request.itemCollection);
    ctx.executeQueryAsync(  processItems.bind(request)  ,failure);
}
function processItems() {
    var received=this;//received 'request' from Caller
    received.items=[];
    var listEnumerator = received.itemCollection.getEnumerator();
    while (listEnumerator.moveNext()) {
        var listitem = listEnumerator.get_current();
        received.items.push(listitem.get_id());
    }
    console.info(received.listName,'has',received.items.length,'items IDs:',received.items);
}
function failure(sender, args){
    console.error(args.get_message());
}
console.clear();
for(var cycle=1;cycle<4;cycle++){// call multiple times to show ASYNC result in the console
    console.log('Calling 3 lists cycle:',cycle);
    getItems('Tasks');
    getItems('Documents');
    getItems('Images');
}

You could have bound just the itemCollection itself, but I elaborated a bit to show how to pass more data.

For (oldschool) CreateDelegate and (since ES-5) bind() differences see: Differing ways to make executeQueryAsync calls - Function.createDelegate (is old IE8 code)

Run this and you will see the asynchronous responses:
(Tasks was requested first, but Documents was received first, etc)

J1 J5 iJSOM TOP5

5
  • Is it at all possible to get all 3 lists within 1 executeAsync? So I have a lot more than 3 lists and want to reduce the number of calls I'm making.
    – Batman
    Aug 16, 2016 at 4:21
  • You would have to go the Search API route then and do one search call. Why worry about the number of calls? It is the same payload size, you only get it in chunks... actually.. it is probably faster .. because they are async Aug 16, 2016 at 7:23
  • I'm trying to create a report that returns all the checked out files in a collection. So I have to make a call for each site, then each library, then get each item in the library that's checked out. It works fine with web services but if I come across a really large collection is slows things down considerably. I've seen it crash a browser before too. So I'm trying to find a way to reduce the number of requests.
    – Batman
    Aug 16, 2016 at 18:26
  • If you switch to REST calls you can change verbose to minimalmetadata, or nometadata and reduce payload Aug 16, 2016 at 20:05
  • Alright, I've give REST a shot and see if there's an improvement over SPServices in terms of overall resources getting the data needs for both.
    – Batman
    Aug 16, 2016 at 21:51
1

As you guessed, the problem is most probably because of messed-up variables called between two different contexts in the onQuerySuccess methods.
One way around this would be to create a JavaScript class to encapsulate all this client API calls.

See for instance https://stackoverflow.com/a/387733/3439544 that explains how to create a class.

In your case, you'd have say a "Query" class. It contains a siteUrl and a listName properties (aka name and gender in the mentionned post). And also a this.listItems initialized but not set from the constructur at the moment.
Methods (prototypes) of this class would be a .Query and both onQuerySuccess and onQueryFailed.

Sample (not tested, just so you get my point):

// Define a class like this
function QueryList(siteUrl, listName){

   // Add object properties like this
   this.siteUrl = siteUrl;
   this.listName = listName;
   this.listItems = null;
}

// Add methods like this.  All Person objects will be able to invoke this
QueryList.prototype.query = function(){
      var context = new SP.ClientContext(this.siteUrl);
      var camlQuery = new SP.CamlQuery();
      var list = context.get_web().get_lists().getByTitle(this.listName);
      [...]
      this.listItems = ...
      context.load(this.listItems);
      context.executeQueryAsync(
             Function.createDelegate(this, this.onQuerySuccess),
             Function.createDelegate(this, this.onQueryFailed)
        );
};

QueryList.prototype.onQuerySuccess = function(...){
 // Access values from this.listItems
 ...
};

QueryList.prototype.onQueryFailed = function(...){
 ...
};



// Usage

// Instantiate new objects with 'new'
var query1 = new QueryList(url, List1);
query1.query();

var query2 = new QueryList(url, List2);
query2.query();
1
  • I edited my answer to add a sample (not tested).
    – Evariste
    Dec 15, 2015 at 16:15
0
 function createLists() {
  extractData(url, List1);
  extractData(url, List2);
}  

function extractData(siteUrl, listName) {
  var context = new SP.ClientContext(siteUrl);
  var camlQuery = new SP.CamlQuery();
  var list = context.get_web().get_lists().getByTitle(listName);
  [...]
  context.load(listItems);
  context.executeQueryAsync(onQuerySuccess,onQueryFailed);
}  

function onQuerySuccess() {
    //Your code here
}

function onQueryFailed() {
    //Your code here
}

Try this should work for you

Your Answer

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

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