【问题标题】:Angularjs for loop issueAngularjs for 循环问题
【发布时间】:2015-06-23 00:35:14
【问题描述】:

有一个 for 循环,在 for 循环内我正在调用 AJAX 请求。我遇到的问题是,for 循环在请求完成之前完成。

我希望 for 循环仅在所需的 AJAX 请求完成后继续进行下一次迭代。

PS-AJAX 工作正常。我确实从服务器获得了我想要的信息。这只是 for 循环迭代首先完成,而无需等待 AJAX 请求成功函数启动。因此,当 AJAX 成功函数最终触发时,变量 cid 中的值是不确定的,因为它已被 for 循环的最后一次迭代覆盖。

我希望 for 循环仅在 AJAX 成功函数执行后继续。

代码:

if (window.cordova) {
    db = $cordovaSQLite.openDB("my.db"); //device
} else {
    db = window.openDatabase("my.db", '1', 'my', 1024 * 1024 * 100); // browser
}

var query = "SELECT * FROM order_product";
$cordovaSQLite.execute(db, query, []).then(function(res) {
    if (res.rows.length > 0) {
        for (var i = 0; i < res.rows.length; i++) {
            console.log(" foreach SELECTED shopcart-> " + res.rows.item(i).id);
            var cid = res.rows.item(i).coffee_id;
            $http.post("http://192.168.1.4/coffeepayWeb/public/index.php/getsugar", {
                cid: cID
            })
            .success(function(result) {
                console.log("success");

                if (cid == 6) {
                  //do something
                } else {
                  //do something else
                }
            });
        }
    }
}

【问题讨论】:

标签: angularjs ionic-framework ionic


【解决方案1】:

如果您需要迭代索引,使用for 对异步操作是不安全的,仅使用它来存储进行异步操作所需的值(在这种情况下为$http.post),它应该如下所示:

var items = [];

for (var i = 0; i < res.rows.length; i++) {
  var item = res.rows.item(i);
  items.push(item);
}

考虑到$http 返回了一个promise,那么您应该能够映射items 中的所有元素

var getsugarUrl = 'http://192.168.1.4/coffeepayWeb/public/index.php/getsugar';

// Map the values to obtain the promises on each $http operation, the allSettled method of the
// [Kriskowal Q library](https://github.com/kriskowal/q/wiki/API-Reference) will be simulated
// this is because when one of the http requests fail then the $q.all method break and reject with an error
var promises = items.map(function (item) {
    var cid = item.coffee_id;
    return $http.post(getsugarUrl, { cid: cid })
      .then(function (result) {

        // You can take advantage of this closure to handle the result of the request an the
        // cid property, store your modified result on the value property from the return

        return {
          state: 'fullfilled',
          value: {
            result: result,
            cid: cid
          } // your modified result
        };
      })

      // Handle when the http request fails
      .catch(function (err) {
        return {
          state: 'rejected',
          error: err
        };
      });
  });

最后处理使用$q.all得到的结果(需要注入$q服务)

$q.all(promises)
  .then(function (responses) {
    // iterate over the results
    responses
      .filter(function(response) { // only fullfilled results
        return response.state == 'fullfilled'; 
      })
      .forEach(function (response) {
        if (response.value.cid == 6) {
          //do something with response.value.result
        } else {
          //do something else
        }
      });
  });

使用此解决方案,http 请求不会按顺序解析,但您可以控制它们何时一起完成,并且您将获得正确的值 cid

查看有关 JavaScript Promises 的更多信息

【讨论】:

  • +1 表示.map 到一组承诺;您甚至可以通过将 .map() 转换为 .reduce() 并使用承诺链(这比递归解决方案 IMO 更好)来实现顺序请求(如果需要的话)。
【解决方案2】:

$http 使用promises,这意味着您需要在 Promise 范式中考虑问题。

考虑一个递归选项,您传入一个 cID 数组,每次调用都会为数组中的第一个 cID 发送一个 $http.post;如果调用成功,我们会继续递归地使用一个较小的数组,直到没有剩余数组为止。

在第一次调用中创建并返回一个 Promise,在每次成功查询时都会收到通知(允许您执行每个 cID 的逻辑),并在所有查询完成时最终解决(如果任何查询失败则拒绝)。

// This function is called without deferred;
// deferred is used on recursive calls inside the function
function doPost(url, cidList, deferred) {
  if (deferred === undefined) {
    deferred = $q.defer();
  }

  var cid = cidList[0];
  $http.post(url, {cid: cid})
  .success(function(result) {
     // query succeeded; notify the promise
     deferred.notify({cid: cid, result: result});

     if (cidList.length > 1) {
       // there are more items to process; make a recursive
       // call with cidList[1:end]
       doPost(url, cidList.slice(1), deferred);
     } else {
       // we're done; resolve the promise
       deferred.resolve();
     }
  })
  .error(function(message) {
    // there was an error; reject the promise
    deferred.reject({cid: cid, message: message});
  });

  return deferred.promise;
}

// build the list of cIDs to pass into doPost
var cidList = [];
for (var i = 0; i < res.rows.length; i++) {
  cidList.push(res.rows.item(i).coffee_id);
}

// start the queries
doPost("http://192.168.1.4/coffeepayWeb/public/index.php/getsugar", cidList)
.then(function() {
  // promise resolved
  console.log("All done!");
}, function(info) {
  // promise rejected
  console.log("Failed on cID " + info.cid + ": " + info.message);
}, function(info) {
  // promise being notified
  console.log("Just did cID " + info.cid + ": " + info.result);

  // your per-cid handler
  if (info.cid == 6) {
    // do something
  } else {
    // do something else
  }
});

更新

由于问题的动机更多地与变量范围有关(而不是顺序 HTTP 请求),这就是您真正需要的:

// Build the CID list so that we can iterate it
var cidList = [];
for (var i = 0; i < res.rows.length; i++) {
  cidList.push(res.rows.item(i).coffee_id);
}

// Iterate the list & call $http.post
cidList.forEach(function(cid) {
  // your $http.post() logic; each call will have its own
  // cid thanks to closures
});

每次迭代都有自己的cid,您可以在.success().error() 处理程序中使用它,而不必担心它会被覆盖。与其他解决方案一样,请求不是连续的,但您可能一开始就不需要它们。

【讨论】:

  • 谢谢;重读您的问题,我意识到您可能并不需要按顺序处理您的 http 请求,所以我更新了一个更简单的选项——@Loen22 首先意识到了这一点:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-05
  • 1970-01-01
  • 2014-03-19
  • 2020-09-15
  • 2011-01-26
相关资源
最近更新 更多