2016-03-19 38 views
5

Nella maggior parte dei violini contenenti codice di utilizzo di esempio per il caricamento di file ng (https://github.com/danialfarid/ng-file-upload) come quello in (http://jsfiddle.net/danialfarid/maqbzv15/1118/), le funzioni di richiamata risposta caricamento eseguono il loro codice in una chiamata di servizio $timeout, ma queste chiamate non hanno alcun ritardo parametro passato in.

la documentazione Angular.js per $timeout (https://docs.angularjs.org/api/ng/service/ $ timeout) indicano che il ritardo è facoltativo, ma perché si vuole fare una chiamata a $timeout se non ritardare l'in esecuzione codice. In altre parole, invece di quanto segue, perché non fare quello dopo:

//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)); 
}); 
} 
}]); 

C'è qualche ragione per la $timeout involucro in tutti questi esempi? Sarebbe il seguente file.upload call lavorare al suo posto ?:

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)); 
}); 

Edit: Vedo che sembra funzionare senza il $timeout involucro, ma il fatto è incluso in tutti gli esempi mi fa pensare che sia intenzionale, che probabilmente significa che c'è un caso di sicurezza/robustezza/compatibilità con i browser che non capisco qui.

risposta

10

Tutto ha a che fare con il ciclo di digest di Angular. Proverò a dimostrarlo con un esempio prima di passare a spiegare che cos'è il ciclo digest. Immaginate il seguente codice:

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

C'è un problema inerente con questo codice. Per quanto cambiamo la variabile di $scope.name dopo 2 secondi, Angular è completamente all'oscuro di questa modifica a $scope.name. Se ora si considera il seguente esempio in cui usiamo $timeout invece:

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

angolare chiamerà la funzione anonima dopo due secondi, però, sarà poi iniziare il ciclo di digerire angolare. Questa è la differenza principale tra $timeout e setTimeout, il ciclo di digest in esecuzione.

Il ciclo di digest è (semplicemente) angolare che passa su tutti gli osservatori (binding), verifica eventuali modifiche e re-rendering laddove appropriato. Potresti aver visto una menzione allo $scope.$apply altrove - ecco come avviare il ciclo di digest.

Per quanto riguarda l'esempio che hai fornito: se il $timeout non è stato utilizzato, angolare non sarebbe a conoscenza che tutte le modifiche sono state fatte e, come tale, la vista non sarebbero aggiornare. Ho menzionato lo $scope.$apply prima, quindi ti starai chiedendo perché non lo usiamo semplicemente? Il problema con l'utilizzo di $scope.$apply è che non puoi essere sicuro che un ciclo di digest non sia già in corso. Se lo chiami mentre uno sta cercando, vedrai un errore "$digest is already in progress". $timeout verrà eseguito solo dopo il ciclo corrente e, in quanto tale, questo errore non si verificherà.

Le persone spesso usano $timeout senza alcun ritardo per notificare ad Angular che è stata apportata una modifica da una terza parte (come il file uploader), che altrimenti non sarebbe stato noto.

Speriamo che questo chiarisca le cose.

Tom

+0

Grazie per questa risposta. Tuttavia, come esplicitamente menzionato dall'OP, "sembra funzionare senza il wrapper $ timeout". Puoi far luce sul motivo per cui funziona ancora senza il wrapper '$ timeout'? – rinogo

+2

Inoltre, in base alle proprie esigenze, si può prendere in considerazione l'uso di '$ scope. $ EvalAsync()' invece di '$ timeout'. Fonte: https://www.bennadel.com/blog/2605-scope-evalasync-vs-timeout-in-angularjs.htm – rinogo

+2

@rinogo Posso solo immaginare un pezzo di codice che sta avviando il ciclo di digest. È impossibile che angular diventi consapevole delle modifiche a '$ scope' a meno che non venga avviato un digest. – maddockst

1

$timeout può essere utilizzato per richiamare qualsiasi richiamata in modo asincrono. per esempio.

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

Ciò significa che tutto il javascript finirà prima che venga richiamata la richiamata. L'aggiunta di un parametro delay allo timeout ritarderà l'invocazione di richiamata circa ancora di più, ma si acquisirà comunque un comportamento asincrono indipendentemente dal fatto che sia fornito o meno un delay.