javascript - 如何在递归 AJAX 调用中保留计数器?

标签 javascript jquery ajax recursion

我有几行项目。每行都有不同数量的项目。 我想要一个计数器来跟踪加载的项目总数。我有一个通过 AJAX 调用递归循环所有行的函数,就像这样(我已经注释掉了与问题无关的内容)

$(document).ready(function () {
    var num_loaded = 0;
    var row = 0;
    load(row, num_loaded);
}

function load(row, num_loaded) {
    var count = num_loaded;
    $.ajax({

        // url and other stuff
        // ...

        success: function (response) {
            for (i = 0; i < response.items.length; i++) {

                // load item[i] 
                // ...

                count++;
            }

            // logic that checks row+1 is not out of bounds
            // ...

            load(row+1, count);
        } 
    })

    console.log(count);
}

如您所见,成功后,每个加载函数都会调用自身并传入一个跟踪计数的参数。这不起作用,我在控制台中不断收到 NaN 或未定义。如何完成此操作并仅查看加载的项目数的最终计数?

最佳答案

您的主要问题是一个相当常见的回调问题,即您将 ajax 视为同步调用,而实际上它是异步的。换句话说,您尝试在调用 ajax 之后立即打印 count,但到那时 count 不会更新。

var count = num_loaded;
$.ajax({
  // ... this updates count some time later
});
// this happens right after we call $.ajax but before the success callback
// so count still has the same value as before we called $.ajax
console.log(count);

这是一个related question你可以看看

此外,您在 load() 方法中定义计数器,这意味着它将在每次函数调用的调用堆栈上重新创建。

如果您通过异步回调递归地传递 count ,则只要递归停止(即达到最大行数的基本情况),它将具有正确的值,因此您必须在回调中打印它:

$(document).ready(function () {
  load(0, 0);
}

function load(row, count) {
   $.ajax({
      // ...    
      success: function(response) {

        // ... loop that increments the count

        if ( /* your base case is reached */ ) {
          console.log(count);
        } else {
          load(row + 1, count);
        }
      }
    });
  }
}

更干净、更合理的方法是使用 Promise。您可以让 load() 方法返回 Promise这将在成功时:

  • 解决加载下一行的 promise ,或者
  • 如果达到基本情况,
  • 打印count

由于 Promise 是可链接的,我们也可以轻松地递归传递 count:

function loadWithPromise(row, count) {
  return new Promise(function(resolve) {
    $.ajax({
      // ...    
      success: function(response) {

        // ... loop that increments the count

        // if we reached the base case, resolve this promise (and thus the whole chain)
        if ( /* your base case is reached */ ) {
          resolve(count);
        } 
        // otherwise load the next promise
        else {
          resolve(loadWithPromise(row + 1, count));
        }
      }
    });
  });
}

// recursively load all rows and then print count once we're done
loadWithPromise(0, 0).then(function(count) {
  console.log(count);
});

如果不需要按顺序加载行,可以使用 Promise.all等待所有对 load() 的调用完成,然后再打印count。这样你就可以将所有的 promise 保存在一个数组中并让它们独立发生,而且你不需要递归。但缺点是您需要在全局范围内定义 count,因为这些 promise 现在是不相关的:

var count = 0;

function loadWithPromise(row) {
  return new Promise(function(resolve) {
    $.ajax({
      // ...    
      success: function(response) {

        // ... loop that increments the count

        resolve();
      }
    });
  });
}

var promises = [];
for (var row = 0; row < MAX_ROWS; row++) {
  promises.push(loadWithPromise(row));
}

// once all loading operations have resolved, print the count
Promise.all(promises).then(function() {
  console.log(count);
});

关于javascript - 如何在递归 AJAX 调用中保留计数器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39283770/

相关文章:

javascript - 仅当另一个函数在 React 中运行完成时才调用一个函数

javascript - 如何在javascript中使用id获取文本框的值

javascript - 添加新模型后渲染

javascript - 如何在使用数据表发送到模板之前获得 AJAX 响应?

javascript - Bootstrap Alert 只显示一次

javascript - 在 Jive 中调整 HTML 小部件的大小

javascript - Firebase 同步 XMLHttpRequest 已弃用

javascript - 如何为html div启用键盘事件?

jquery - 屏幕大小元素的 HTML 结构

jquery - PreventDefault() 上的必填字段