0

I have a button which when clicked opens a modal(dashboard_name) in which user enters some value. Based on the value after he clicks submit on that modal I call another function which opens another modal and user enters a different value there and finally when he clicks submit on this modal, I call an api to verify everything is correct.

Now, the problem is when I click on the first button to open the modal the execution doesn't wait for the function to get the data from the dashboard_name modal and then the graph_name modal. Instead it directly jumps to the api function call, which is right coz that's how jQuery works. But I wanted to know how to use deferred and promise to make this execution serial.

Function for the first button when clicked.

$('#add_to_dash').click(function(e){
  dashboard_submit();
  graph_submit();      
});

this function gets the dashboard modal and tries to get the value.

function dashboard_submit(){
  //do something
}

this function after success of the dashboard_submit function tries to get value for the graph modal

function graph_submit(){
  //do something
}

and then on form submit i call this following func

<form name="form2" onsubmit="return isDashboardCorrect(dashboard_name);" method="post" action="{{ url_for('dashboards_new') }}">

the function

function isDashboardCorrect(dashboard_name) {
  var flag=0;
  $.ajax({
    async: false,
    type: 'GET',
    url: 'xyz.com/dashboard/'+dashboard_name,
    success: function(data) {
      //alert(data);
      //do something
   });
}

I want all of this to be sequential which is not happening right now i.e. when i click on the first button it doesn`t wait for the functions to execute and directly the isdashboardcorrect() function gets called.

I want the order to be 1. button click 2. dashboard_submit() 3. graph_submit() 4. isdashboardcorrect() serially.

I tried something simpler like

$('#add_to_dash').click(function(e){
  alert('addtodashstart');
  dashboard_submit().done(function(){
    alert('done');
  });


  alert('addtodashend');
});


function dashboard_submit()
{
  alert('dashboardsubmot');
  var dfd = new $.Deferred();
  $("#d_name_modal_div").modal({backdrop: false}).modal("show");
  $('#d_name_modal_submit').on('click', function(){
    dashboard_name=$('#dashboard_name').val();
    alert(dashboard_name);
    if(dashboard_name==null || dashboard_name.trim()=='')
    {
      alert('Dashboard name is mandatory.');
      return false;
    }
    else
    {
      dfd.resolve();
      return dfd.promise();
    }
  });
}

When I click the button I call the dashboard_submit function. But here too it doesn`t wait for

`$('#d_name_modal_submit').on('click', function(){  

this to execute in the above function and directly hits the api function. What Am i doing wrong?`

Example fiddle : http://jsfiddle.net/LKP66/18/

KungWaz
  • 1,918
  • 3
  • 36
  • 61
TommyT
  • 1,707
  • 3
  • 17
  • 26
  • In Javascript, you cannot "pause" execution to wait until an async operation is done. Instead, you have to code using asynchronous notification callbacks so that you can finish your operation when the Ajax call is done. It is a different way of coding, but not hard once you learn. – jfriend00 Mar 18 '15 at 23:09
  • Can you point me into the right direction. If you know any blogs or examples? – TommyT Mar 18 '15 at 23:10
  • So many code very few would read the whole thing. Just nest the second call inside the success callback of your first ajax call or sync the two calls via `jQuery.Deferred` – kidwon Mar 18 '15 at 23:10
  • Is there any simple tutorial for deferred, i am having a hard time understanding the concept since I am very new to jscript. – TommyT Mar 18 '15 at 23:15
  • give me a few minutes I will give you an example mock up – kidwon Mar 18 '15 at 23:17
  • I guess that modal is a bootstrap modal. Use that jsfiddle: http://jsfiddle.net/omje4uaj/ to provide the above example – kidwon Mar 19 '15 at 00:51
  • Yes it is @kidwon. http://jsfiddle.net/LKP66/18/ – TommyT Mar 19 '15 at 18:54

2 Answers2

0
  $('#add_to_dash').click(function(e) {

    $.when(dashboard_submit()).then(function(){
      graph_submit();
    });

  }); 

  function dashboard_submit() {
    var dfd = new $.Deferred();
    $.ajax({
      ....,
      ....,
      success: function (....) {
        dfd.resolve();
        /*some code...*/
      }
    });

    return dfd.promise();
  }

dfd is an object that can have a few outcomes same as $.ajax() which also returns deferred, actually. What are ajax outcome? success, error, complete.... and you have callbacks on them. Same thing with an instance of $.Deferred. However you have a control over what and when the outcome is resolve, reject,....etc. base on your code. As you can see I have receive successfully the data and I call that it was resolved successfully dfd.resolve();. So you can consider .then() and .done() equivalent to success both called on resolve. The difference is that then() is a method of dfd.promise().then() while done() is a method of dfd itself dfd.done()

Ex.2

  $('#add_to_dash').click(function(e) {

    dashboard_submit().done(function(){
      graph_submit();
    });

    /* That is 
     dfd.done(function(){
       graph_submit();
     });         
     since dashboard_submit() returns dfd
    */

  }); 

  function dashboard_submit() {
    var dfd = new $.Deferred();
    $.ajax({
      ....,
      ....,
      success: function (....) {
        dfd.resolve();
        /*some code...*/
      }
    });

    return dfd;
  }

Ex.3

As I mentioned $.ajax() returns $.Deferred instance so no need to create explicitly one so actually dfd isn't needed you are provided by one already so:

  $('#add_to_dash').click(function(e) {

    dashboard_submit().done(function(){
      graph_submit();
    });

  }); 

  function dashboard_submit() {
    return $.ajax({
      ....,
      ....,
      success: function (....) {

        /* The dfd that ajax returns is resolved on success by jQuery so no explicit instance creation is needed nor resolving*/
      }
    });
  } 
kidwon
  • 4,448
  • 5
  • 28
  • 45
  • Hey. Thanks a lot @kidwon. Can you explain it a little bit especially the deferred.resolve() and .done() functions. Thanks. – TommyT Mar 18 '15 at 23:31
  • 2
    This is not a particularly good design pattern. `$.ajax()` already returns a promise so you don't need to create your own in any way. It also doesn't solve the OP's issue as it's still a promise callback that tells them when they're done. And, `when()` is not a function in jQuery, it's `$.when()`, but it isn't need either because you can use `.then()` directly on the returned promise. Sorry, but this is not a good example of how to do things. – jfriend00 Mar 18 '15 at 23:34
  • @jfriend00 indeed but he doesn't have a clue spare us here he should have an explicit example. Thanks for the `$.when` appreciate it :) – kidwon Mar 18 '15 at 23:44
  • 2
    @kidwon - While promise are very useful to learn, you really don't want to learn them this way because this is the wrong way to use them. Here is some reading on promise anti-patterns, [What is the deferred antipattern and how do I avoid it?](http://stackoverflow.com/questions/23803743/what-is-the-deferred-antipattern-and-how-do-i-avoid-it), [Promise anti-patterns](https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns) and [Promise Anti-Patterns](http://taoofcode.net/promise-anti-patterns/). – jfriend00 Mar 18 '15 at 23:58
  • 3
    On the `$.when()`, you don't need it at all. You can just do `dashboard_submit().then(graph_submit)`. In your code, `dashboard_submit` returns a promise so you can just directly use `.then()` on it. No need for `$.when()`. `$.when()` is useful when you have multiple promises and you want a callback when all of them are done. It's of no use for a single promise. – jfriend00 Mar 19 '15 at 00:01
0

About your second question: You don't get it att all what I've explain it to you, do you?

done() subscription callbacks are called when you resolve your defferred instance dfd. It is called almost the very moment you resolve it. So you are going to open your next dialog when you have recieved the data.

Just take 1-2 hours and try to understand the concept and what I've explained to you. That is very poor programming you are doing there.

http://jsfiddle.net/qoytmgcj/2/

function dashboard_submit() {
     alert('Dashboard submit');
    var dfd = new $.Deferred();
    dashboard_name = 'ram';
     $("#d_name_modal_div").modal({
        backdrop: false
    });

    dfd.done(function(){
        var dn = $('#dashboard_name');
        dn.val('booooooo I recived the data now I can open the dialog');
        dn.text(dn.val());
        $("#d_name_modal_div").modal("show");
    });
    dfd.resolve();
    return dfd;
}

function wtf() {
    var dfd = $.Deferred();
    $('#d_name_modal_submit').on('click', function () {
        dashboard_name = $('#dashboard_name').val();
        alert(dashboard_name);
        if ( !$.trim(dashboard_name) ) {
            alert('Dashboard name is mandatory.');
            return false;
        } else {
            dfd.resolve();
            alert('success');
            return dfd;
        }
    });
} 


$('#add_to_dash').click(function (e) {

    alert('Add to dash start');
    dashboard_submit().done(function () {
        wtf();
    });
    alert('Add to dash end');
});
kidwon
  • 4,448
  • 5
  • 28
  • 45
  • I wish I was better with javascript, but I am working on it. Thanks for the help @Kidwon. – TommyT Mar 20 '15 at 17:16
  • Kidwon, did you really mean to `return dfd` from wtf's click handler? – Roamer-1888 Mar 20 '15 at 20:37
  • @Roamer-1888 No where to return it to there, but that is @TommyT code from the fiddle I just modified it a little to demonstrate. So yes that return is pointless , more sense to do it in `wtf` function if used later. The only reason to return a `dfd` is to resolve it later in one's code or to subscribe to its resolvence. If not no need to return. – kidwon Mar 21 '15 at 12:48