【问题标题】:Function in while loop executes only oncewhile循环中的函数只执行一次
【发布时间】:2015-11-12 15:37:24
【问题描述】:

我是 javascript 的初学者,我想弄清楚为什么我的 while 循环实际上不会循环多次,即使条件总是满足。

我有一个发送 API 请求的函数:

var get_status = function(trid, count) {
            console.log(count);
            var req = {
                method: 'GET',
                url: 'theUrlHere',
                headers: {'headers'}
            }
            $http(req).success(function(data) {
                if (data.transaction_status != 'Pending') {
                       // do something with the data
                    console.log('true');
                    return true;
                }
                else {
                    console.log('False');
                    return false;
                }
            }).error(function(data) {
                      // show an error popup
                console.log('true');
                return true;
            })
        }
    };

我想调用这个函数直到它返回true,所以我这样称呼它:

var count = 0;
while (get_status(id, count) === false) {
    count += 1;
}

count 变量只是添加来查看它循环了多少次,即使控制台中显示“False”,它也保持为 0。

我在这里误解了一些行为吗?

编辑我明白为什么这不起作用。我的意图是只要交易状态处于待处理状态,就显示 iframe。我想过不断发送请求,直到交易状态不是“待处理”,但我知道还有更优化的方法。

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

    您的get_status() 函数没有返回值。因此,它的返回值为 undefined,这是错误的,因此您的 while() 循环在第一次迭代后停止。

    您在代码中的返回语句位于回调内部,与get_status() 的返回值无关。


    您尝试做的通常不是一个好的设计。看来您希望一次又一次地运行给定的 Ajax 调用,直到得到您想要的答案。这可能会影响目标服务器。

    如果您描述了您真正想要解决的问题,我们可以帮助您想出一个更好的方法来解决这个问题。最坏的情况是,您可能会在请求之间有时间延迟来轮询服务器。

    如果你想每隔一段时间轮询一次,你可以这样做:

    function get_status(trid, count) {
        var req = {
            method: 'GET',
            url: 'theUrlHere',
            headers: {'headers'}
        }
        return $http(req).then(function(data) {
            return data.transaction_status;
        });
    }
    
    function poll_status(callback) {
        function next() {
            get_status(...).then(function(status) {
                if (status === "Pending") {
                    // poll once every two seconds
                    setTimeout(next, 2000);
                } else {
                    // status is no longer pending, so call the callback and pass it the status
                    callback(status);
                }
            }, function(err) {
                callback(err);
            });
        }
        next();
    }
    
    
    poll_status(function(result) {
        // done polling here, status no longer Pending
    });
    

    【讨论】:

    • 关于你的第二点,正确的做法是什么?
    • @Guillaume 如果你想“轮询”服务器,你可以使用下面的 while 方法,但 callback() 在定义的时间间隔内来自 setTimeout
    • @Guillaume - 正如我在回答中添加的那样,如果您可以描述更多关于您实际尝试解决的问题,我们可能会提出比您尝试的更好的设计.
    • @Guillaume - 我添加了一个轮询选项。我个人不喜欢轮询(通常效率低下),但您没有对您的系统进行足够的解释,不知道您如何能够比轮询更好地做到这一点。
    • @jfriend00 谢谢你的建议,我认为这最符合我的意图。我理解为什么轮询通常是一种不好的方法,但是我目前没有更好的主意。只要交易状态处于待处理状态,我就需要在我的页面上显示一个 iframe,并且服务器从第三方获取状态。除了经常轮询服务器以检查状态是否已更改之外,我无法想出其他方法
    【解决方案2】:

    这不是处理异步调用的正确方法,我会创建一个会调用自身的递归函数。 (在这种情况下,get_status 应该返回一个承诺)

    代码

    var count = 0, id = 1;//id should be some value
    (function myCall(promise){}
       promise.then(function(data){
          count += 1;
          if(data)
            myCall(get_status(id, count)); //call function on conditon
       });
    }(get_status(id, count))
    

    方法(返回承诺)

    var get_status = function(trid, count) {
            console.log(count);
            var req = {
                method: 'GET',
                url: 'theUrlHere',
                headers: {'headers'}
            }
            //returning promise here
            return $http(req).then(function(response) {
                var data = response.data;
                if (data.transaction_status != 'Pending') {
                       // do something with the data
                    console.log('true');
                    return true; //resolves the promise
                }
                else {
                    console.log('False');
                    return false; //resolves the promise
                }
            }, function(data) {
                      // show an error popup
                console.log('true');
                return true;
            })
        }
    };
    

    【讨论】:

      【解决方案3】:

      您正在尝试从异步回调中返回,但不幸的是,这不起作用。相反,您需要像 async 这样的模块,特别是 whilst

      var count = 0;
      var outcome = false;
      
      async.whilst(
          function () { outcome = false; },
          function (callback) {
              count++;
              // Your code here, setting outcome instead of returning
              var req = {
                  method: 'GET',
                  url: 'theUrlHere',
                  headers: {'headers'}
              }
              $http(req).success(function(data) {
                  if (data.transaction_status != 'Pending') {
                      outcome = true;
                      callback();
                  }
                  else {
                      outcome = false
                      callback();
                  }
              }).error(function(data) {
                  outcome = true;
                  callback();
              })    
          },
          function (err) {
              // All done!
          }
      );
      

      但实际上,您正在寻找的行为可能是以预定义的时间间隔检查状态。在这种情况下,适配代码

      var count = 0;
      var outcome = false;
      
      async.whilst(
          function () { outcome = false; },
          function (callback) {
              count++;
              // Your request stuff.
              setTimeout(function () {
                 callback();
              }, 1000); // Waits one second to begin next request
          },
          function (err) {
              // All done!
          }
      );
      

      【讨论】:

      • 谢谢,我明白我的错误在这里,我会看看这个
      • 这绝对会一遍又一遍地敲击目标服务器。这几乎总是一个糟糕的设计。
      • @jfriend00 OP 没有询问糟糕的设计。但这比原来的要好——它只会在一个完成后开始一个新的请求。正如我在评论中提到的,这是构建此设计的最佳方法之一,OP 可以根据需要使用 setTimeout。
      • 好的答案提供了好的设计选择。对设计不佳的问题提供字面答案是一个答案,但不是一个好的答案。
      • @Guillaume 我已经更新以说明您的用例可能是什么。
      最近更新 更多