为啥我会像在 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 的摘要循环。这是 $timeout
和 setTimeout
之间的主要区别,正在运行的摘要循环。
摘要循环是(简单地说)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 服务中包装一个函数,而不会延迟?的主要内容,如果未能解决你的问题,请参考以下文章