JQuery Deferred compatibility

Would it be possible to alter SPServices so that it returns an object that can be used with JQuery's Deferred (http://api.jquery.com/category/deferred-object/)? Right now it's harder than it needs
to be to make several asynchronous calls with SPServices.

Yes. The SPServices calls use jQuery.ajax underneath the covers, do they not? You can simply return the jqXHR object rather than discarding it (which is what happens currently). My understanding is that that object can be used with the Deferred functionality.

So every call do $().SPServices would return that underlying jqXHR object, allowing you to do something like
:

.done() was just an example. A much more important use is being able to schedule something to happen upon completion of two or more asynchronous requests have been fired off. See http://api.jquery.com/jQuery.when/.

Although I agree that having SPServices calls return a Deferred .promise() would be helpful, I don't agree that it should be the default behavior (should @sympmarc decide to implement it)... Specially if SPServices() currently returns a jquery object which
allows for chaining (I don't remember if it does).

But... You can also use jQuery's Deferred() object along with SPServices calls to accomplish what you are asking for here... Yes, it is a "little" more coding.. but not much more... Just wrap SPServices calls in a deferred object and return the .promise()...
Here is an example I have used often in the past:

The Deferred functionality in jQuery is quite advanced and I don't claim to know all the ways to use it... I believe that in resolving the object you can also set pieces of information for the down stream queued operations...Use this approach allot when
controlling UX after what I call "compound" updates to multiple lists.

Thanks Paul. This example is helpful. I still think it would simplify things a lot to simply get the underlying jqXHR object though. My biggest issue with this method is the requirement of calling dfd.resolve() in the completefunc(). Ideally, the callback
function shouldn't know or care that it is part of a larger chain of async calls.

Ok, so to be more specific about use cases, I have a control which fires off AJAX requests using SPServices for data about a list schema, about content types, and about items in various other lists. Right now, I have a lot of ugly, poorly maintainable code
which causes some logic to run only when all of the requests have come back successfully. By using JQuery's Deferred object, I can use the .when() method, which is a heck-of-a-lot nicer, and because it's part of JQuery its
very maintainable.

It's very common that you'd want to parallelize a bunch of AJAX calls. JQuery's Deferred makes doing so a lot nicer looking than doing this manually.

I've read this thread and then re-read this thread. My initial reaction was NO, don't change how the results are sent back. That may potentially break a whole bunch of existing code, which may force people to go through a painful upgrade.

After reading through the thread again, it occurred to me... SPServices is open source. If you wanted this functionality in your own toolkit, add it in. There's nothing stopping you from doing that and creating a more maintainable codebase for your project(s).

There is another option for you as well ( maybe )... That would be to use the
$.ajaxSetup() options. You can then specify what your calls should do when they fail. SPServices only uses the "complete" callback, so you shouldn't interfere with anything in the library.

@iOnline247 I think you're misunderstanding the request. Right now, many SPServices calls return
nothing. My feature request is to have them return the underlying jqXHR object rather than return nothing. For those SPServices calls that immediately return a value, those obviously can not be changed.

Even if a library is open source, there are huge drawbacks to customizing it yourself. Namely- you are forever forking it from the main project, so you won't be able to get new versions and bug fixes.

I don't see what you are getting at, but I do feel there are more drawbacks to changing the core AJAX function of this library than forking a copy. Not to mention all of the issues that crop up when a new jQuery version comes out.

All of the bug fixes/new versions have been open source as well. It seems like all you need to do is tweak to the $.ajax call, so I don't see that being much of a pain to add in to a copy of your own or copy/paste into a new version. It's definitely more
work than clicking download, I agree with you there.

@iOnline247: I'll show some code since I don't seem to be getting my point across:

function myCode(someOtherListId) {
/* Ideally, I should be able to do this call SPListNameFromUrl asynchronously
* as well, perhaps add an optional "async" parameter
* which, if used, returns the jqXHR object and invokes
* a callback upon completion rather than blocking until
* the data returns. This is not the main point of this code though.
*/var currentListId = $().SPServices.SPListNameFromUrl();
//Call 1var a = $().SPServices({
operation: "GetListItems",
async: true,
listName: currentListId,
completefunc: function(xData) {
//Do something useful
}
});
//Call 2var b = $().SPServices({
operation: "GetList",
async: true,
listName: currentListId,
completefunc: function(xData) {
//Do something useful
}
});
//Call 3 var c = $().SPServices({
operation: "GetListItems",
async: true,
listName: someOtherListId,
completefunc: function(xData) {
//Do something useful
}
});
/* Now I want to do something once all 3
* asynchronous calls have completed. Under the current
* model, there's no easy way to do this.
* I either have to hand-roll some ugly code,
* or contort my completefuncs so that they work with
* jQuery's Deferred.
*/
}

At the end of these 3 async calls, variables a, b, and c will all be empty.
SPServices calls currently don't return anything. Making them return the jqXHR object, instead of nothing, is
not a breaking change. It won't break a single line of your, or anyone else's, code.

Summary: Asynchronous programming is very ugly and difficult to maintain and jQuery.Deferred is very helpful towards making it more sane. Exposing the underlying jqXHR object
which is currently being discarded facilitates better async programming and is not a breaking change.

"Exposing the underlying jqXHR object which is currently being discarded facilitates better async programming" <-- Agreed.

"and is not a breaking change." <-- Are you sure? You've tried already? :-D

What I've done in the past when I've had to do multiple async request is nest them but use very descriptive function names. That way when stepping through my code, it's less painful and a lot more obvious. Here's an example:

Sadly, what you describe is in JS CSOM as well to some degree ( It is much better in some ways ). We have to do the same thing. Wait for the request to come back and then nest our logic in the callback. I'm not totally against the change, I just
feel the effort to change the library may not be worth it. Then there's regression testing. It all sounds like work...

JavaScript does suck to maintain when you have a lot of it. Trust me, I know...

Nesting the logic in the callback is not equivalent to the example I gave. The example I gave makes the 3 asynchronous calls
in parallel. Nesting the calls makes them happen in serial, which greatly increase latency. The "hand-rolled" way to accomplish what I described is have each callback register that it has completed, and upon that registration some logic is
called, but it only continues executing if all 3 registrations have happened. This code is much uglier than simple nested of callbacks. I suggest you look further into asynchronous programming to understand the differences as they are important.

Your logic about breaking changes is basically akin to arguing that a library should
never add new features because, in your opinion, all changes are breaking changes. If you are so convinced that
any change is a breaking change, I encourage you to come up with a concrete example of how adding a return value will break existing JavaScript code. (Note, in a compiled language, adding a return value to a public API (where there was no return value
prior) is a breaking change as the method signature will have changed, but JavaScript is not compiled).

Guys, make nice. I think it's very useful to watch you discuss this. I see where both of you are coming from, but I'm still not fully sure that what MgSam is looking for makes sense as a change to SPServices yet. On the other hand, I can also see how
it might improve some of the things I've implemented with SPServices, especially when in comes to our old friend IE.