【问题标题】:chained jQuery ajax calls firing in wrong order链接的 jQuery ajax 调用以错误的顺序触发
【发布时间】:2018-03-05 00:46:53
【问题描述】:

我有一个选择列表。每个选项都是一组短文本。选择特定捆绑包时,文本将显示在两个表格中。每行都有一个“删除”图标,以便可以从包中删除文本。我希望表和选择列表在删除后刷新。共有三个链式调用:

  1. 从数据库中删除文本>>

  2. 刷新选择列表并在自定义标签中转储一些数据>>

  3. 从自定义标签中获取数据并重建表

但他们似乎按 3 >> 1 >> 2 的顺序开火。我试图重现的解决方案是 this。我做错了什么?

感谢您的帮助!

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

更新

1 和 2 肯定是按顺序执行的(请参阅screenshot)。问题在于 3('createSegTableRows')。这是不调用服务器的那个。

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

这是代码。 (损坏的位是第一个sn-p中的最后一个块。)

sn-p 1:当从选择列表中选择一个选项时触发:

// 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");
        });
      }
    });
  }
}

sn-p 2:这会重新填充选择列表:

// 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("");
      }
    }
  );
}

【问题讨论】:

  • 'ajax/bundle_delete_join.php' 是立即返回响应,还是仅在删除数据库记录后才发送响应? 'bundleSelector' 不是一个承诺。所以它会被调用并立即返回。我怀疑您正在向服务器发出请求,服务器立即响应,然后调用 2 并立即返回,触发 3,然后先完成 3,然后是 1,然后是 2,因为 3 是最快的,然后是 1,然后是 2。
  • 您有 .then() 和 .done() 是否有特殊原因?
  • James,它看起来像 'bundle_delete_join.php' 它是数据库更新后返回的响应。我不认为问题出在 php 端。基本上有两件事要更新:选择列表和表格。选择列表直接从数据库中更新,并且表现正确。该表应该从选择列表中 html 中的某些数据更新,这就是问题所在。听起来你是对的。
  • Izzy,如果我对此比较陌生(尤其是 ajax 链)。我的印象是“完成”有效地关闭了链条,但看起来我错了。

标签: javascript jquery ajax promise chained


【解决方案1】:

这里的关键在于理解bundleSelector()异步的

在“不起作用的位”中,您正确地return bundleSelector();,但该函数返回undefined。为了在bundleSelector() 完成后运行某些东西,它必须返回一个承诺,并且为了让createSegTableRows() 按预期工作,该承诺不仅必须在其$.post() 返回时履行,而且在所有选项构建时都必须履行完成了。

要实现这一点,bundleSelector() 的 $.post(..., fn) 调用必须修改为return $.post(...).then(fn),否则调用者将不会等待选项构建完成,然后继续createSegTableRows() ;因此您报告的 3-1-2 执行顺序。

【讨论】:

  • Roamer-1888,非常感谢您的回复!这实际上很有意义,但我仍然无法让它发挥作用。只是修改了 bundleSelector() 的内部来设置一些变量; return $.post(url, { stuff... }).then(function(data){ 循环并更新 html... });我得到了和以前一样的行为。
  • 最终弄清楚了为什么它不起作用。这真是愚蠢的事情。我忘了考虑 bundleSelector() 不返回任何结果的可能性,因此下一个函数中的 JSON 解析器在发送“未定义”后停止。再次感谢您的帮助!
  • 我想知道你是否会发现它!实际上,bundleSelector() 中至少有两个可能的失败案例需要处理; 1) 没有结果; 2) data 是假的。有很多方法可以处理这些情况,其中最简单的方法是强制 bundleSelector() 返回的承诺解决其错误路径。这样,您可以调用$.post(...).then(bundleSelector).then(createSegTableRows) 并确保createSegTableRows() 只有在bundleSelector() 成功时才会运行。
  • 谢谢。 Juts 修复了虚假的 data 东西。 createSegTableRows() 实际上并不直接从 bundleSelector() 获取数据。 bundleSelector() 将数据转储为附加到 HTML 元素上的自定义标记的字符串; createSegTableRows() 从那里收集它(就像一堆其他函数一样)。我现在已经设置好了,如果bundleSelector() 不能处理数据,它只是清空自定义标签,createSegTableRows() 适当地返回一个空表。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-30
  • 1970-01-01
  • 2011-07-03
  • 2014-11-09
  • 1970-01-01
  • 2011-04-04
  • 1970-01-01
相关资源
最近更新 更多