0

I've written a plugin that is supposed to fetch error messages from an API and display them inside an element's HTML. I want to implement it as a promise, so that other events can be chained to it via a done or always callback. The simplified version of the plugin looks like this:

(function( $ ) {
     $.fn.flashAlerts = function() {
         var deferred = $.Deferred();

         var field = $(this);

         $.getJSON( "alerts", {})
         .done(function( data ) {
             // Display alerts
             field.html(data['message']);

             deferred.resolve();
         });
         return deferred.promise();
     };
 }( jQuery ));     

And a use scenario might look like this:

$("#myForm").submit(function(e) {
    e.preventDefault();

    var form = $(this);

    // Disable submit button
    form.disableSubmitButton();

    var serializedData = form.serialize();
    var url = form.attr('action');

    $.ajax({  
     type: "POST",  
     url: url,  
     data: serializedData       
    }).fail(function(jqXHR) {

       // Display errors on failure
       $('#alerts').flashAlerts().done(function() {
           // Re-enable submit button
           form.enableSubmitButton();
       });
    });
});

The idea is to enforce the following sequence of actions:

  1. POST the form. Before the request, disable the submit button. When the request is complete,
  2. GET the errors from the alerts api. Once that request is complete, display the error messages. After the error messages have been displayed,
  3. Re-enable the submit button.

This code seems to work as it stands, but I have no idea if the deferrals are working in the correct order (or if I'm just getting lucky with the timing of the requests).

So, am I doing this correctly? How could I test that the deferral order is as desired?

alexw
  • 8,468
  • 6
  • 54
  • 86
  • As a quick and dirty solution try console logging in the body of the done callbacks. From there you could also just check the status of the submit button too. – Jeff Mar 05 '15 at 04:10
  • If you were using something to write unit tests -- like jasmine or qunit -- there would be a cleaner way to do it. – Jeff Mar 05 '15 at 04:10
  • What is expected `context` , return value of `$.fn.flashAlerts()` ? – guest271314 Mar 05 '15 at 04:19

1 Answers1

1

This code seems to work as it stands, but I have no idea if the deferrals are working in the correct order

Yes they do work as expected.

So, am I doing this correctly?

No, you are using the deferred antipattern in your flashAlerts methods. Additionally, you ignore errors from the ajax request to /alerts, so if both requests fail the button won't be re-enabled. Not sure whether that's your intent, but I'd use .always instead of .done for the last callback.

Here's what I'd write:

 $.fn.flashAlerts = function() {
     var field = $(this);
     return $.getJSON( "alerts", {})
     .then(function(data) {
         // Display alerts
         field.html(data['message']);
         return data;
     });
 };

How could I test that the deferral order is as desired?

There's not much to test. Through the way your code is layed out, it is entirely impossible that the form.enableSubmitButton(); would be called before $('#alerts').flashAlerts() or that the $.getJSON( "alerts", {}) would be called before $.ajax({ … }). Of course you have to trust those functions that they don't resolve their promises before they're done (as is required for your code to work), but the order of execution is unsurmountable.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375