Home & blog  /  Tag: AJAX  /

Non-AJAX use for jQuery's deferred objects

posted: 09 Apr '12 19:41 tags: jQuery, deferred, AJAX

I'm currently writing another article for .NET magazine on the newer features of jQuery, for developers who perhaps got comfy with jQuery 1.3 or 1.4 and didn't keep up.

One of the obvious candidates for the article is deferred objects, which landed in jQuery 1.5 as part of the overhaul to jQuery's AJAX module.

AJAX is the obvious use-case for deferred objects, and it's simple to come up with examples for that. But I was also trying to show a non-AJAX example.

I'm talking about cases where you would manually make your own deferred objects and apply subjective logic as to whether, when and how it is resolved or rejected. So other forms of asynchronous code execution.

This is quite a different beast from using deferreds with AJAX, since, at least usually, jQuery's AJAX methods automatically create, return and resolve/reject deferred objects for you. In other words, you can use deferreds in an AJAX context without ever going near methods like $.Deferred(), $.when() or deferred.resolve().

Click the vowels

I eventually came up with a slightly contrived game for children where they have to identify and click the vowels in a sequence of words. Each vowel would constitute a deferred object. When clicked, the vowel fades out and its deferred object is set to resolved. When all deferreds are resolved (i.e. all vowels have been clicked), we give feedback and move on. I think it's quite a nice pattern.

You can see a demo of the game here.

First, some simple HTML:

1

Click all the vowel letters

2

'words'>

And CSS:

1#words { height: 100px; }

2#words div { display: inline-block; width: 100px; height: 100%; text-align: center; font-size: 50px; line-height: 90px; margin-right: 10px; background: #e50; color: #fff; cursor: default; }

3#words div:last-child { margin: 0; }

Now on to the JS (all inside a DRH, of course, as we're dealing with the DOM).

1//prep

2var

3words = ['square', 'hosepipe', 'canine', 'flower'],

4container = $('#words'),

5vowels = 'aeiou',

6next_word_index = 0;

All rather self-explanatory. Now for the bulk of the code:

1function build_word(word) {

2    

3     //increment the next-word index

4     next_word_index++;

5    

6     //remove the previous word, if any

7     container.empty();

8    

9     //an array to store our deferreds (one for each vowel)

10     var deferreds = [];

11    

12     //loop over the word's letters

13     for (var i=0, len=word.length; i

14        

15         var

16         letter = word.substr(i, 1),

17         isVowel = vowels.indexOf(letter) != -1,

18         letter_div = $('

', {text: letter}).appendTo(container);

19            

20         //if this letter is a vowel...

21         if (isVowel) {

22            

23             //set up a deferred object for it and log it in the array

24             var deferred = $.Deferred();

25             deferreds.push(deferred);

26            

27             //on click, fade it out then resolve its deferred

28             letter_div.click({deferred: deferred}, function(evt) {

29                 $(this).animate({opacity: .2}, evt.data.deferred.resolve).unbind('click');

30             });

31         }

32     }

33    

34     //when all deferreds are resolved, do feedback and move on

35     $.when.apply(null, deferreds).done(function() {

36         var msg = 'Well done - you got all the vowels! ';

37         if (words[next_word_index]) {

38             alert(msg+"Let's try the next word...");

39             build_word(words[next_word_index]);

40         } else

41             alert(msg+"That's the end of the game!");

42     });

43}

44    

45//on page entry, do the first word

46build_word(words[next_word_index]);

A few points

Hopefully the comments make it possible to follow what's going on there, but here's some points of particular note.

Firstly, I invoke $.when not directly but via the native apply(). This is because $.when() does not presently allow you to pass multiple objects as an array, which is necessary for my example. apply(), as you may know, allows you to stipulate arguments to a function as an array, so problem solved.

(If you're new to $.when(), I'll be covering that in a separate post, as it has a lot to offer your patterns.)

Secondly, in a production environment it would be prudent to expose not the deferreds themselves but their inherant promise objects (via the promise() method) instead. This allows environmental code to bind callbacks to them but not interfere with their state or progress. See the jQuery API page on promise objects for more detail.

Summary

I reiterate that this is a slightly contrived example, to highlight the use of deferreds independently of AJAX, but I think it's quite a nice pattern.

Of course, the same effect could be achieved several other ways without deferreds; one could continually check, in the fadeout callback, whether there were any vowels still remaining at full opacity. If no, the user has clicked all the vowels. That would require a more complex fadeout callback, but it would of course work.

5 comments | post new

posted: 02 Dec '10 18:55 tags: XML, JSON, remap, convert, data, AJAX, jQuery

I've had a great response to my XML-to-JSON convertor and remapper. Its ability to not only convert but also remap data on the fly, to your precise structural requirements, has gone down particularly well.

One flaw raised today, though, was that it didn't work with RSS fees. Standard XML feeds, yes, but not RSS. After some digging I realised why.

The plugin expected the iteration node (i.e. the repeated node whose data will form the sub-objects of your JSON data) to directly follow the root node, e.g.

1

2    

3         foo

4         bar.html

5    

6

But if your XML had more levels - such as the following, typical of RSS:

1

2    

3        

4             foo

5             bar.html

6        

7    

8

...it didn't play ball. This has been countered by the addition of a new second argument when calling the plugin, in which you pass a space-separated selector string matching the iteration node, so for the above two examples this would be 'root news_story' and 'rss channel news_story', respectively.

Happy days! to download, get usage info or view a demo.

I've also been asked about whether the plugin supports XML node attributes. No, but it will. Stay posted...

XML-to-JSON convertor and remapper

posted: 24 Nov '10 22:45 tags: XML, JSON, remap, convert, data, AJAX, jQuery

After much hair-pulling and cursing, my XML-to-JSON convertor and remapper is finally here.

The real power behind this plugin is its ability to not only convert but also REMAP your data on the fly. There are other plugins out there that handle conversion, but I've not seen any that also remap.

This has the potential to be hugely useful.

Imagine you're using a news ticker that requires JSON data (for the headlines, URLs etc), but you want to populate it with data from an XML feed you don't have control over, and whose property names and hierarchical structure is different from what your ticker needs:

1

2     foo

3     bar

4    

5         foobar

6    

7

but you need

1{

2     one: 'foo',

3     two: 'bar',

4     three: 'foobar'

5}

...so not only a conversion but also a structural change. You can achieve that with this plugin.

Head over here to download, get usage info or view a demo.

post a comment

UI for Postcode Anywhere address look-up

posted: 08 Jun '10 15:44 tags: address, AJAX, cURL, PHP, form, Javascript, jQuery

Just posted this in the scripts section. It's a user interface for the popular and very useful Postcode Anywhere address look-up service.

They seem to like it - they tweeted about it earlier.

Head over here to download, get usage info or view a demo.

The focus, as ever, is on integration. You set a few config vars, call the script into your page and that's it. You even need to prepare any elements; the look-up link or button is DOM-scripted into your page next to your postcode field, while returned addresses are displayed as links in a neat scrolling

that is DOM-scripted under your field.

post a comment