为啥我会像在 ng-file-upload 示例使用代码中那样在 AngularJS $timeout 服务中包装一个函数,而不会延迟?

Posted

技术标签:

【中文标题】为啥我会像在 ng-file-upload 示例使用代码中那样在 AngularJS $timeout 服务中包装一个函数,而不会延迟?【英文标题】:Why would I wrap a function in AngularJS $timeout service without a delay like they do in ng-file-upload example usage code?为什么我会像在 ng-file-upload 示例使用代码中那样在 AngularJS $timeout 服务中包装一个函数,而不会延迟? 【发布时间】:2016-07-06 14:40:14 【问题描述】:

在大多数包含 ng-file-upload (https://github.com/danialfarid/ng-file-upload) 示例使用代码的小提琴中,例如 (http://jsfiddle.net/danialfarid/maqbzv15/1118/) 中的那个,上传响应回调函数将它们的代码包装在 $timeout 服务调用中,但是这些调用没有传入任何延迟参数。

$timeout (https://docs.angularjs.org/api/ng/service/$timeout) 的 Angular.js 文档表明延迟是可选的,但如果不是为了延迟正在运行的代码,为什么还要调用 $timeout。换句话说,而不是下面的,为什么不做后面的:

//inject angular file upload directives and services.
var app = angular.module('fileUpload', ['ngFileUpload']);

app.controller('MyCtrl', ['$scope', 'Upload', '$timeout', function ($scope, Upload, $timeout) 
$scope.uploadPic = function(file) 
file.upload = Upload.upload(
  url: 'https://angular-file-upload-cors-srv.appspot.com/upload',
  data: username: $scope.username, file: file,
);

file.upload.then(function (response) 
  $timeout(function () 
    file.result = response.data;
  );
, function (response) 
  if (response.status > 0)
    $scope.errorMsg = response.status + ': ' + response.data;
, function (evt) 
  // Math.min is to fix IE which reports 200% sometimes
  file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total));
);

]);

在所有这些示例中,$timeout 包装器是否有任何理由?下面的 file.upload 调用会代替它工作吗?:

file.upload.then(function (response) 
  file.result = response.data;
, function (response) 
  if (response.status > 0)
    $scope.errorMsg = response.status + ': ' + response.data;
, function (evt) 
  // Math.min is to fix IE which reports 200% sometimes
  file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total));
);

编辑:我可以看到它似乎在没有 $timeout 包装器的情况下运行,但它包含在所有示例中的事实让我认为这是故意的,这可能意味着存在安全性/稳健性/浏览器兼容性边缘情况我不这里不明白。

【问题讨论】:

【参考方案1】:

这与 Angular 的摘要循环有关。在继续解释什么是摘要循环之前,我将尝试通过一个示例来证明这一点。想象一下下面的代码:

angular.module('app', []).controller('TestController', ['$scope', function($scope)
    $scope.name = 'Tom';
    setTimeout(function()
        $scope.name = 'Bob';
    , 2000);
]);

此代码存在固有问题。尽管我们在 2 秒后更改了 $scope.name 的变量,Angular 完全不知道对 $scope.name 的更改。如果您现在考虑以下示例,我们使用$timeout 代替:

angular.module('app', []).controller('TestController', ['$scope', '$timeout', function($scope, $timeout)
    $scope.name = 'Tom';
    $timeout(function()
        $scope.name = 'Bob';
    , 2000);
]);

Angular 将在两秒后调用匿名函数,然而,它将开始 Angular 的摘要循环。这是 $timeoutsetTimeout 之间的主要区别,正在运行的摘要循环。

摘要循环是(简单地说)Angular 遍历所有观察者(绑定),检查任何更改并在适当的地方重新渲染。您可能已经在其他地方看到过对 $scope.$apply 的提及 - 这是开始摘要循环的方法。

关于您提供的示例:如果未使用 $timeout,Angular 将不会意识到已进行任何更改,因此您的视图将不会更新。我之前提到过$scope.$apply,所以你可能想知道为什么我们不直接使用它呢?使用$scope.$apply 的问题在于您无法确定摘要周期尚未进行。如果你在一个发生时调用它,你会看到一个错误“$digest is already in progress”。 $timeout 只会在当前循环之后运行,因此不会发生此错误。

人们经常使用$timeout 来通知 Angular 第三方(比如你的文件上传者)已经做出了更改,否则它不会知道发生了。

希望这可以解决问题。

汤姆

【讨论】:

感谢您的回答。但是,正如 OP 明确提到的,“它似乎在没有 $timeout 包装器的情况下运行”。你能解释一下为什么没有 $timeout 包装器它仍然可以工作吗? 另外,根据您的需要,您可以考虑使用$scope.$evalAsync() 而不是$timeout。来源:bennadel.com/blog/… @rinogo 我只能想象其他地方的一段代码正在启动摘要周期。除非启动摘要,否则 Angular 不可能意识到 $scope 的更改。【参考方案2】:

$timeout 可用于异步调用任何回调。例如

$timeout(function callback() 
  // code run asynchronously...
);

这意味着所有 javascript 将在您的回调被调用之前完成运行。将delay 参数添加到timeout 将进一步延迟回调调用,但无论是否提供delay,您仍然可以获得异步行为。

【讨论】:

以上是关于为啥我会像在 ng-file-upload 示例使用代码中那样在 AngularJS $timeout 服务中包装一个函数,而不会延迟?的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 http-server wiki 示例

为啥我会在这个程序中“中止”? [复制]

为啥我会在这里收到类型安全警告? (泛型)

AngularJS 文件上传控件 ng-file-upload

为啥我会得到欧洲/柏林时区 0:53 的偏移量?

ngf 前缀在 angularjs 和 ng-file-upload 中是啥意思?