chained jQuery ajax calls firing in wrong order

蹲街弑〆低调 提交于 2019-12-24 22:05:47

问题


I have a select list. Each option is a bundle of short texts. When a specific bundle is selected, the texts are displayed in two tables. Each row has a "Delete" icon, so that a text can be removed from the bundle. I want both the tables and the select list to refresh after deletion. There are three chained calls:

  1. delete text from db >>

  2. refresh select list and dump some data in a custom tag >>

  3. get data from custom tag and rebuild tables

But they seem to be firing in the order 3 >> 1 >> 2. The solution I am trying to reproduce is this. What am I doing wrong?

Thank you for your help!

~~~~~~~~~~~~~

UPDATE

1 and 2 are definitely executing sequentially (see screenshot). The problem is with 3 ('createSegTableRows'). This is the one that does NOT make a call to the server.

~~~~~~~~~~~~~

Here's the code. (The broken bit is the last block in the first snippet.)

snippet 1: This fires when an option is selected from the select list:

// This fires when a bundle is selected.
function selectBundle() {

  createSegTableRows();
  rowIconActions();

  // this creates some rows for a couple of tables
  function createSegTableRows() {

    // get "value" from the currently selected option
    var bundle_id = $('select#bundleSelector').find(':selected').prop('value');

    // get some strigified JSON data stored in a custom tag
    var inTagData = $('select#bundleSelector').find(':selected').attr('data');
    // convert back to a JSON object
    var inTagData_parsed = JSON.parse(inTagData);

    // convert some data from inside the JSON object to HTML table rows 
    var st_table_contents = tableRows(inTagData_parsed.st);
    var tt_table_contents = tableRows(inTagData_parsed.tt);

    // populate the tables
    $('#st_seg_table').html(st_table_contents);
    $('#tt_seg_table').html(tt_table_contents);

    // this converts JSON data into table rows
    function tableRows(rObj) {

      // map to array and sort
      var rArray = $.map(rObj, function(el) {
        return el;
      });
      rArray.sort(function(a, b) {
        return a.join_id > b.join_id;
      });

      //	create rows	
      var rows = ""
      for (i = 0; i < rArray.length; i++) {
        var segment_id = rArray[i]['segment_id'];
        var join_id = rArray[i]['join_id'];
        var segment_text = rArray[i]['seg_text'];

        // each row has some text and Up/Down/Delete buttons
        rows += "<tr><td class='sid tt'>" + segment_id + " <a title='Up' jid='" + join_id + "'>&#9650</a><a title='Down' jid='" + join_id + "'>&#9660</a> <a title='Remove' jid='" + join_id + "'>&#10005</a> </td><td>" + segment_text + "</td></tr>";
      }

      return rows;
    }
    console.log("some table rows");
  }

  // actions fired by Up/Down/Delete in each row
  function rowIconActions() {

    // find selected option in a <select> list
    var bundle_id = $('select#bundleSelector').find(':selected').prop('value');

    // attach an action to the Delete buttons in each table row 
    $('td.sid>a[title="Remove"]').click(function() {

      var join_id = $(this).attr('jid');
      var role = $(this).parent().prop('className').split(" ")[1];

      // THIS IS THE BIT THAT DOESN'T WORK
      if (join_id && bundle_id) {
        $.post(
          // delete record in db
          'ajax/bundle_delete_join.php', {
            bid: bundle_id,
            jid: join_id
            // rebuild <select> list
          }).then(function() {
          return bundleSelector();
          console.log("some stuff");
          // rebuild tables
        }).done(function() {
          createSegTableRows();
          console.log("done");
        });
      }
    });
  }
}

snippet 2: This repopulates the select list:

// This repopulates the select list.
function bundleSelector() {

  if ($("#pairButton").text("Unpair")) {
    var pid = $("#pairButton").attr("pairid");
  }

  $.post(
    // collect some criteria and retrieve stuff from db
    'ajax/bundle_selector.php', {
      st: $("#txtId_left").html(),
      tt: $("#txtId_right").html(),
      pair: pid,
      filter_st: $('#bundleFilterCheck_st').prop('checked'),
      filter_tt: $('#bundleFilterCheck_tt').prop('checked'),
      filter_pair: $('#bundleFilterCheck_pair').prop('checked')
    },
    function(data) {

      if (data) {

        // convert results to a JSON object
        var dataObj = JSON.parse(data);

        // create a variable for the options
        var options = '';

        if (dataObj != "") {

          // loop through the JSON object...
          Object.keys(dataObj).forEach(key => {

            var bundle_id = key;
            var role = dataObj[key];

            options = options + "<option value='" + bundle_id + "' data='" + JSON.stringify(role) + "'>bundle " + key;

            // loop some more...
            Object.keys(role).forEach(key => {

              if (role[key] && key != 'comment' && JSON.stringify(role[key]) != '[]') {

                options = options + " | " + key + ":";

                var segment_id = role[key];

                // convert to an array for sorting
                var joinDataArray = $.map(segment_id, function(el) {
                  return el;
                });

                // sort the array
                joinDataArray.sort(function(a, b) {
                  return a.join_id > b.join_id;
                });

                // loop through the array
                for (i = 0; i < joinDataArray.length; i++) {

                  var sid = joinDataArray[i]['segment_id'];

                  options = options + " " + sid;

                }
              }
            });
            // add a closing tag to each option
            options = options + "</option>";
          });
          // populate parent element
          $('select#bundleSelector').html(options);
          console.log("some select options");

        } else {
          // if there are no results...
          $('select#bundleSelector').html("");
          $('table#st_seg_table').html("");
          $('table#tt_seg_table').html("");
          $('textarea#bundle_comment').html("");
        }
      } else {
        // and again
        $('select#bundleSelector').html("");
        $('table#st_seg_table').html("");
        $('table#tt_seg_table').html("");
        $('textarea#bundle_comment').html("");
      }
    }
  );
}

回答1:


The key here lies in understanding that bundleSelector() is asynchronous.

In "THE BIT THAT DOESN'T WORK" you correctly return bundleSelector(); but that function returns undefined. In order for something to run after bundleSelector() has completed, it must return a promise and, for createSegTableRows() to work as expected, that promise must fulfill not only when its $.post() has returned but also when all the option-building is complete.

To achieve that, bundleSelector()'s $.post(..., fn) call must be modified to return $.post(...).then(fn), otherwise the caller will not wait for the option-building to complete before proceeding to createSegTableRows(); hence the 3-1-2 execution order you report.



来源:https://stackoverflow.com/questions/46381648/chained-jquery-ajax-calls-firing-in-wrong-order

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!