0

I have asked myself this question several times before:

Is it possible to have a JavaScript function used multiple times from multiple locations returning a promise calling ctx.executeQueryAsync, but only doing a single request to server from browser?

This is not the same as just wrapping executeQueryAsync in a promise, as that would possibly result in multiple requests to server.
It is also the opposite of a normal Q.all-function which waits for several promises to finish. Instead I want to wait for one single promise to finish then call out to a random amount of "listeners".

The reason I post an answer to my own question is because I haven't seen it come up before, and I find it really neat and a nice-to have solution which I want to share.

1

My starting point is to have one shared instance of SP.ClientContext. I'm going to augment the object instance so using new keyword is preferable:

var ctx = new SP.ClientContext();

The basic theory is to create a method execThen returning a promise, called when executeQueryAsync finishes, but only once for each "listener".
Something like this:

Usage

ctx.load(ctx.get_web());

ctx.execThen().then(function() {
  console.log(ctx.get_web());
});

ctx.load(ctx.get_site());

ctx.execThen().then(function() {
  console.log(ctx.get_site());
});

The code above will issue one single call to executeQueryAsync, even though execThen is called twice (in separate locations, could be separate files). If a new execThen was made inside of another execThen there would be a new call to executeQueryAsync, e.g.,

ctx.execThen().then(ctx.execThen);

Any promise-based library should support this, such as jQuery, Angular or Q.
As an example; here is my current implementation in Angular:

Angular example

ctx.execThen = (function () {
  var q;
  return function () {
    if (!q) {
      q = $q.when().then(function () {
        return $q(function (resolve, reject) {
          ctx.executeQueryAsync(resolve, reject);
        }).then(function () {
          q = null;
        });
      });
    }
    return q;
  };
})();
  • 1
    I'm not sure I can see the value/usefulness of this. Can you please provide a broader context/scenario/example that let us better understand where you find this can be useful? – MdMazzotti May 18 '15 at 9:13
  • @eirikb, i have to agree with MdMazzotti, could you please explain a little bit with a more realistic example? – Vadim Gremyachev May 18 '15 at 9:34
  • 1
    @MdMazzotti Consider this code could render something like this, but only make one single browser request – eirikb May 18 '15 at 20:57
  • @eirkb Maybe I'm missing something, but what I still don't get is the timing. When exactly does executeQueryAsync get called and why? From the code, I can see you return a new promise the first time the execThen function is called; all subsequent calls get the same promise. What if executeAsync is run before a later call to .load is made? – MdMazzotti May 21 '15 at 7:18
  • @MdMazzotti The first call to execThen will create only one promise which will be returned on subsequent calls. Because of the additional .when() the call to executeQueryAsync will not be issued until the next Angular $digest cycle. So with the cost of a tiny delay it's possible to add .load-calls directly after execThen. – eirikb May 21 '15 at 8:01

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.