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

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 (https://github.com/danialfarid/ng-file-upload) like the one at (http://jsfiddle.net/danialfarid/maqbzv15/1118/) 示例使用代码的小提琴中,上传响应回调函数将其代码包装在 $timeout 服务调用中,但这些调用确实没有传入任何延迟参数。

$timeout (https://docs.angularjs.org/api/ng/service/$timeout) 的 Angular.js 文档表明延迟是可选的,但是如果不要延迟代码 运行。换句话说,为什么不执行以下操作:

//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 包装器,但它包含在所有示例中的事实让我认为这是故意的,这可能意味着有一个 security/robustness/browser 兼容性边缘情况我在这里不明白。

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

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

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

这与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 第三方(例如您的文件上传者)进行了更改,否则它不会知道已经发生。

希望这能解决问题。

汤姆