Is there a JavaScript preprocessor that makes callbacks look pretty?

JavaScript toolkit like jQuery deals with callback functions, and these callbacks are often defined anonymously. Example. Some web pages display a list of messages in a table. To update this table, you can first query the server for a list of all current messages (as IDs), and then retrieve the content for the as-yet-unknown message IDs:

function fnUpdateMessages() {
   $.ajax({
      type: 'POST',
      data: { action: 'get_message_ids' },
      success: function(sData) {
         var aMessageIds = sData.split(/,/);
         var aUnknownIds = fnWhichIdsAreNotInTable(aMessageIds);
         $.ajax({
            type: 'POST',
            data: {
               action: 'get_message_contents',
               ids: aUnknownIds.join(',')
            },
            success: function(oData) {
               for (var id in oData.messages) {
                  fnInsertMessage(oData.messages[id]);
               }
            }
         );
      }
   );
}

      

Can you see where I'm going? This code is ugly because the indentation is at level 6 after two subsequent AJAX calls. Of course, I can split the anonymous functions into separate functions in the file scope, but that usually pollutes the namespace (unless it clutters the stuff by wrapping it up with another anonymous function call) and it breaks the tight coupling between those functions: callbacks should really not used by themselves; they are similar to the second and third parts of the original function fnUpdateMessages

.

What I really want is something like this:

function fnUpdateMessages() {
   $.ajax({
      type: 'POST',
      data: { action: 'get_message_ids' },
      success: continue(sData)
   });

   var aMessageIds = sData.split(/,/);
   var aUnknownIds = fnWhichIdsAreNotInTable(aMessageIds);
   $.ajax({
      type: 'POST',
      data: {
         action: 'get_message_contents',
         ids: aUnknownIds.join(',')
      },
      success: continue(oData)
   );

   for (var id in oData.messages) {
      fnInsertMessage(oData.messages[id]);
   }
}

      

This snippet introduces a new hypothetical syntax continue(var1, var2, [...])

that defines an anonymous callback function whose body is whatever follows in the application scope. This makes these callback functions look like synchronous code. This would need to be preprocessed, obviously, since this is not standard JS.

Before I even think about writing such a preprocessor, I would like to know if something like this exists now?

PS If you like this idea please steal it. At the moment, I cannot afford another project. Linking to your repository in the comment would be great if you jump into some working code.

+3


source to share


3 answers


There are only two solutions:

The first is really bad: you have to make the first ajax request synchronous, but your script will block until the result is available. This is a really bad decision, you shouldn't make any ajax requests synchronous.

The second uses the jQuery.pipe function to return a deferred $ .ajax object (you need to use jquery> 1.5). You can chain callbacks using this pipe (I am using an inner function to make it more readable):



[EDIT]: as of jquery 1.8, you have to use deferred. Then, instead of the deferred .pipe:

    function fnUpdateMessages() {
        var getMessages = function() {
            return $.ajax({
                type: 'POST',
                data: { action: 'get_message_ids' },
            });
        };

        var getContents = function(aUnknownIds) {
            return $.ajax({
                type: 'POST',
                data: {
                    action: 'get_message_contents',
                    ids: aUnknownIds.join(',')
                },
            });
        };

        var insertMessages = function(oData) {
            for (var id in oData.messages) {
                fnInsertMessage(oData.messages[id]);
            }
        };

        getMessages()
            .then(getContents)
            .done(insertMessages);
     }

      

+1


source


You can use lazy jQuery to bind callbacks rather than include them in parameters.

function fnUpdateMessages() {
   $.ajax({
      type: 'POST',
      data: { action: 'get_message_ids' }
   ).done(function(sData) {
      var aMessageIds = sData.split(/,/);
      var aUnknownIds = fnWhichIdsAreNotInTable(aMessageIds);
      $.ajax({
         type: 'POST',
         data: {
            action: 'get_message_contents',
            ids: aUnknownIds.join(',')
         }
      }).done(function(oData) {
         for (var id in oData.messages) {
            fnInsertMessage(oData.messages[id]);
         }
      });
   });
}

      



It's not ideal, but it will save you a couple of levels of indentation per request.

See $ documentation for details . ajax .

+1


source


Yes there is. It's called jwacs - JavaScript with extended continuation support . Simply put, you use continuations to suspend the execution of your program. Then you can resume execution of the program by calling continuation. A continuation always saves the state of the program at the time it was created.

It's a bit like a trampoline in JavaScript , but the trampoline depends on generators that are only supported by Mozilla's Firefox and Rhino products. If you're interested in a trampoline, I wrote a library to make recording asynchronous bearable. It was called Fiber and it looks a bit like Java co-threads.

On the other hand, jwacs compiles to regular JavaScript. Hence, it can be used on any platform. Not just Firefox and Rhino. If you want to understand what continuations are, I suggest you read the following StackOverflow question and answer:

Question: What is the difference between a continuation and a callback?

Answer: fooobar.com/questions/52471 / ...

+1


source







All Articles