angularjs 在 $http 成功中打破 forEach

Posted

技术标签:

【中文标题】angularjs 在 $http 成功中打破 forEach【英文标题】:angularjs break forEach in $http success 【发布时间】:2015-11-16 21:05:12 【问题描述】:

我在 Ionic 框架中有以下代码,

var stopScan = false;
$scope.StopScan = function() 
 stopScan = true;
;

$scope.ScanContacts = function() 

 Contacts.unchecked().then(function(contacts) 

  var promise = $q.all(null);
  angular.forEach(contacts, function(contact) 

    promise = promise.then(function() 

     return $http.post(apiEndpoint+'/check', number: contact.number)
      .success(function(res) 

        Contacts.update(contact.id, res);
        if(stopScan)
         // do break loop;
       )

       .error(function(err) 
        console.log(err);
       );
     );
   );
  );
 ;

它确实是在循环中同步发送 http 请求,并在 $http 错误时中断,就像我想要的那样。但是我如何在 $http 成功中打破循环?我试过throw 'Scan stopped';$q.reject('Scan stopped'); 但没有成功。

【问题讨论】:

我不太清楚你想要实现什么整体目标。但我认为最好将异步 http post 调用包装到 promise 中并相应地调用 resolve 或 reject。关于打破循环,我在您的代码中看不到任何循环。但是您始终可以执行 if-else 语句来检查 stopScan 值。如果是真的,只需调用 break 将打破 JS 中的循环 在你的例子中,stopScan 已经是true - 那么,为什么还要循环呢?但总的来说,stopScan 应该来自哪里? 【参考方案1】:

首先angular.forEach 不支持中断(参见here 和here)

第二break语句必须直接嵌套在循环内,即使是forwhile循环。

最后.success 是异步发生的,循环执行之后,所以通过其他方式打破那里本来是反正没意义。

您似乎希望 stopScan 在其他地方异步设置(例如,响应用户的点击),但您必须确定停止的确切含义 - 是否意味着“不做任何更多 $http.post requests”,或者它的意思是“发出所有请求,但不处理响应?”。 (您的示例似乎暗示了后者,因为您试图在 .success 中处理它,但您应该知道,POST 通常意味着在服务器上进行了更改。

您必须了解,一旦启动 HTTP 请求,它就会发出(或者它处于待处理状态,取决于最大连接数,这取决于浏览器)。

所以,你可以做的是一次并行地触发所有请求,然后手动“超时”($http 支持基于承诺的timeout)那些尚未完成的请求:

var stopScanTimeout = $q(function(resolve)
  $scope.stopScan = function()
    resolve();
  
)

var promises = [];
angular.forEach(contacts, function(contact) 
  var httpPromise = $http( method:  "POST",
                            url:     apiEndpoint+'/check', 
                            data:    number: contact.number,
                            timeout: stopScanTimeout )
                         .then(function(response) return response.data; ,
                               function(error)    return error: error;);
  promises.push(httpPromise);
);

然后你可以一起处理所有的结果,如果它们没有及时完成,有些会是“错误”(但“软”错误):

$q.all(promises).then(function(results)
  for (var i = 0; i < results.length, i++)
    var result = results[i];

    if (result.error) continue;

    // otherwise, process the result
    Contacts.update(contact.id, result);
  
)

【讨论】:

【参考方案2】:

如果您想使用并行 HTTP 请求运行,请使用 @NewDev 的答案。

但是,如果您想坚持串行请求,那么“跳出循环”再简单不过了。

您需要做的就是抛出,它不会因此而中断,但会将构造的 Promise 链发送到错误路径。在停止点,不会有未返回的请求,也不会再发送请求。

我会写这样的东西,使用contacts.reduce(...) 来构建链。

$scope.ScanContacts = function() 
    return Contacts.unchecked().then(function(contacts) 
        return contacts.reduce(function (p, contact) 
            return p.then(function() 
                return $http.post(apiEndpoint + '/check',  number: contact.number )
                .then(function(res) 
                    if(stopScan) throw new Error('scan stopped');
                    Contacts.update(contact.id, res);//you can choose to service the last response or not but placing this line above or below the throw line.
                , function(err) 
                    // As the second .then param, this callback will catch any http errors but not the 'scan stopped' error.
                    // By catching http errors, the scan will be allows to continue.
                    // To stop on http error, either remove this callback or rethrow the error.
                    console.log(err);
                );
            );
        , $q.when());
    );
;

Here's evidence 投掷会产生所需的“停止”效果。

如果 throw 在实际代码中不起作用,那么似乎还有其他问题。

【讨论】:

唯一的问题是你在真正应该成功的事情上抛出了一个错误(我相信;我不确定stopScan 是否应该是一个错误)。是的,您可以立即捕捉到它,但从语义上讲,这有点不对劲。 @trysis,为了流量控制而扔进一个承诺链是完全合法的。

以上是关于angularjs 在 $http 成功中打破 forEach的主要内容,如果未能解决你的问题,请参考以下文章

为啥不推荐使用 AngularJS $http 成功/错误方法?从 v1.6 中删除?

为啥不推荐使用 AngularJS $http 成功/错误方法?从 v1.6 中删除?

angularJS中的$http.get错误——成功不是函数[重复]

$ http get不会在AngularJS中抛出成功或错误[重复]

在 AngularJS 中缓存 HTTP 'Get' 服务响应?

如何在 Angular js 中禁用哈希重定向