" $已经申请正在进行"用Firefox打开时确认对话框 [英] "$apply already in progress" when opening confirm dialog box with Firefox

查看:180
本文介绍了" $已经申请正在进行"用Firefox打开时确认对话框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我(有时)得到一个奇怪的 $已经在进行中适用的错误在以下和无辜样子情况打开一个确认对话框时:

\r
\r

VAR MOD = angular.module('应用',[]);\r
\r
mod.controller('CTRL',函数($范围,$区间,$ HTTP){\r
  \r
  $ scope.started = FALSE;\r
  $ scope.counter = 0;\r
  \r
  //被一再呼吁一些业务功能\r
  //(这里:一个简单的计数器增加)\r
  $间隔(函数(){\r
    $ scope.counter ++;\r
  },1000);\r
  \r
  //这个函数启动一些服务在后端\r
  $ scope.start =功能(){\r
    如果(确认(你确定吗?')){\r
      返回$ http.post('start.do'),然后(功能(RES){\r
        $ scope.started = TRUE;\r
        返回res.data;\r
      });\r
    };\r
  };\r
\r
  //这个功能将停止在后台的一些服务\r
  $ scope.stop =功能(){\r
    如果(确认(你确定吗?')){\r
      返回$ http.post('stop.do'),然后(功能(RES){\r
        $ scope.started = FALSE;\r
        返回res.data;\r
      });\r
    };\r
  };\r
\r
});\r
\r
//的$ HTTP模拟应对snipset沙箱(无关,请无视)\r
mod.factory('$ HTTP',函数($ Q){\r
  返回{\r
    岗位:功能(){\r
      返回$ q.when({数据:空});\r
    }\r
  }\r
});

\r

&LT;脚本SRC =htt​​ps://ajax.googleapis.com/ajax /libs/jquery/1.11.1/jquery.min.js\"></script>\r
&所述; SCRIPT SRC =htt​​ps://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js&GT;&下; /脚本&GT;\r
\r
&LT; D​​IV NG-应用=应用程序&GT;\r
  &LT; D​​IV NG控制器=CTRL&GT;\r
    &LT;按钮NG-禁用=开始NG点击=开始()&GT;开始&LT; /按钮&GT;\r
    &LT;按钮NG-禁用=!开始了NG点击=停止()&GT;停止&LT; /按钮&GT;\r
    &LT; BR /&GT;&LT; BR /&GT;经过秒:{{计}}\r
  &LT; / DIV&GT;\r
&LT; / DIV&GT;

\r

\r
\r

错误消息是:


  

$ rootScope:inprog] $已经在进行申请 http://errors.angularjs.org/1.2 0.23 / $ rootScope / inprog?P0 =%24apply


和调用堆栈是:

  minErr /&LT; @ angular.js:78:12
beginPhase@angular.js:12981:1
$ RootScopeProvider / $这个GET&LT; /Scope.prototype.$apply@angular.js:12770:11
tick@angular.js:9040:25
$scope.start@controller.js:153:8
Parser.prototype.functionCall /&LT; @ angular.js:10836:15
ngEventHandler /&LT; /其中p @ angular.js:19094:17
$ RootScopeProvider / $这个GET&LT; /Scope.prototype.$eval@angular.js:12673:16
$ RootScopeProvider / $这个GET&LT; /Scope.prototype.$apply@angular.js:12771:18
ngEventHandler /&LT; @ angular.js:19093:15
jQuery.event.dispatch@lib/jquery/jquery-1.11.2.js:4664:15
jQuery.event.add/elemData.handle@lib/jquery/jquery-1.11.2.js:4333:6

要重现:


  • 使用火狐
  • (我不能与Chrome或IE浏览器重现)
  • 打开JavaScript控制台

  • 点击或者在启动的和的停止的按钮(确认对话框)

  • 尝试多次(10-20x),不容易发生它

问题消失,如果我删除确认对话框。

我已阅读 AngularJS文档有关此错误(以及其他问题计算器),但我看不出这种情况如何适用,因为我不调用$申请我也不直接与DOM交互。


解决方案

在一些分析,这似乎是在Firefox中$间隔和模态对话框之间的一个令人惊讶的互动。

这是什么问题?

调用堆栈显示一些奇怪的事情:一个AngularJS功能的的是控制器的启动的函数中调用。这怎么可能?

好吧,似乎 Firefox不显示一个模式对话框时挂起超时/间隔时间功能:这允许配置的超时时间和间隔时间的回调被称为在当前执行的javascript code的顶部

在上述情况下,的启动的函数调用一个的 $适用的顺序(由AngularJS当按钮被点击的启动),当的 $间隔的回调在上面执行的启动的功能,第二的 $适用的序列被启动(由AngularJS)=>热潮,一个的 $申请已正在进行中的错误的上升。

可能的解决方案

定义一个新的确认的服务(改编自<一个href=\"http://www.bennadel.com/blog/2808-asking-the-user-to-confirm-location-or-route-changes-in-angularjs.htm\"相对=nofollow>这个和<一个href=\"http://www.bennadel.com/blog/2632-creating-asynchronous-alerts-prompts-and-confirms-in-angularjs.htm\"相对=nofollow>这博客文章):

  //这个工厂定义了一个异步包装为本地确认()方法。它返回一个
//承诺,将解决,如果用户同意的确认;要么
//将是拒绝,如果用户取消确认。
mod.factory(确认功能($窗口,$ Q $超时){    //定义基于承诺 - 确认()方法。
    功能确认(消息){
        变种延迟= $ q.defer();        $超时(函数(){
            如果($ window.confirm(消息)){
                defer.resolve(真);
            }
            其他{
                defer.reject(假);
            }
        },0,FALSE);        返回defer.promise;
    }    返回确认;
});

...,并使用它通过以下方式:

  //这个函数启动一些服务在后端
$ scope.start =功能(){
  确认(你确定吗?')。然后(函数(){
    $ http.post('start.do'),然后(功能(RES){
      $ scope.started = TRUE;
    });
  });
};//这个功能将停止在后台的一些服务
$ scope.stop =功能(){
  确认(你确定吗?')。然后(函数(){
    $ http.post('stop.do'),然后(功能(RES){
      $ scope.started = FALSE;
    });
  });
};

此解决方案,因为模式对话框是一个区间的回调执行中打开了,(我相信)间隔/超时执行会由JavaScript虚拟机序列化。

I am (sometimes) getting a weird $apply already in progress error when opening a confirm dialog box in the following and innocent looking situation :

var mod = angular.module('app', []);

mod.controller('ctrl', function($scope, $interval, $http) {
  
  $scope.started = false;
  $scope.counter = 0;
  
  // some business function that is called repeatedly
  // (here: a simple counter increase)
  $interval(function() {
    $scope.counter++;
  }, 1000);
  
  // this function starts some service on the backend
  $scope.start = function() {
    if(confirm('Are you sure ?')) {
      return $http.post('start.do').then(function (res) {
        $scope.started = true;
        return res.data;
      });
    };
  };

  // this function stops some service on the backend
  $scope.stop = function() {
    if(confirm('Are you sure ?')) {
      return $http.post('stop.do').then(function (res) {
        $scope.started = false;
        return res.data;
      });
    };
  };

});

// mock of the $http to cope with snipset sandbox (irrelevant, please ignore)
mod.factory('$http', function ($q) {
  return {
    post: function() {
      return $q.when({data:null});
    }
  }
});

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="app">
  <div ng-controller="ctrl">
    <button ng-disabled="started" ng-click="start()">Start</button>
    <button ng-disabled="!started" ng-click="stop()">Stop</button>
    <br/><br/>seconds elapsed : {{counter}}
  </div>
</div>

The error message is :

$rootScope:inprog] $apply already in progress http://errors.angularjs.org/1.2.23/$rootScope/inprog?p0=%24apply

And the callstack is :

minErr/<@angular.js:78:12
beginPhase@angular.js:12981:1
$RootScopeProvider/this.$get</Scope.prototype.$apply@angular.js:12770:11
tick@angular.js:9040:25
$scope.start@controller.js:153:8
Parser.prototype.functionCall/<@angular.js:10836:15
ngEventHandler/</<@angular.js:19094:17
$RootScopeProvider/this.$get</Scope.prototype.$eval@angular.js:12673:16
$RootScopeProvider/this.$get</Scope.prototype.$apply@angular.js:12771:18
ngEventHandler/<@angular.js:19093:15
jQuery.event.dispatch@lib/jquery/jquery-1.11.2.js:4664:15
jQuery.event.add/elemData.handle@lib/jquery/jquery-1.11.2.js:4333:6

To reproduce :

  • use Firefox (I could not reproduce it with Chrome or IE)
  • open the javascript console
  • click alternatively the start and stop buttons (and confirm the dialogs)
  • try multiple times (10-20x), it does not occur easily

The problem goes away if I remove the confirm dialog box.

I have read AngularJS documentation about this error (as well as other stackoverflow questions), but I do not see how this situation applies as I do not call $apply nor do I interact directly with the DOM.

解决方案

After some analysis, it seems to be a surprising interaction between the $interval and modal dialog in Firefox.

What is the problem ?

The callstack shows something strange : an AngularJS function tick is called within the controller's start function. How is that possible ?

Well, it seems that Firefox does not suspend the timeout/interval functions when displaying a modal dialog box : this allows configured timeout and intervals callback to be called on the top of the currently executing javascript code.

In the above situation, the start function is called with an $apply sequence (initiated by AngularJS when the button was clicked) and when the $interval callback is executed on the top the start function, a second $apply sequence is initiated (by AngularJS) => boom, an $apply already in progress error is raised.

A possible solution

Define a new confirm service (adapted from this and that blog posts) :

// This factory defines an asynchronous wrapper to the native confirm() method. It returns a
// promise that will be "resolved" if the user agrees to the confirmation; or
// will be "rejected" if the user cancels the confirmation.
mod.factory("confirm", function ($window, $q, $timeout) {

    // Define promise-based confirm() method.
    function confirm(message) {
        var defer = $q.defer();

        $timeout(function () {
            if ($window.confirm(message)) {
                defer.resolve(true);
            }
            else {
                defer.reject(false);
            }
        }, 0, false);

        return defer.promise;
    }

    return confirm;
});

... and use it the following way :

// this function starts some service on the backend
$scope.start = function() {
  confirm('Are you sure ?').then(function () {
    $http.post('start.do').then(function (res) {
      $scope.started = true;
    });
  });
};

// this function stops some service on the backend
$scope.stop = function() {
  confirm('Are you sure ?').then(function () {
    $http.post('stop.do').then(function (res) {
      $scope.started = false;
    });
  });
};

This solution works because the modal dialog box is opened within an interval's callback execution, and (I believe) interval/timeout execution are serialized by the javascript VM.

这篇关于&QUOT; $已经申请正在进行&QUOT;用Firefox打开时确认对话框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆