Below is the code I use in my project to allow user to upload file(s). For each file selected , "$scope.upload" function gets invoked asynchronously.
Because these are asynchronous calls , each call does not depend on previous call.
But my requirement is only after success call back function gets executed, then the next upload should happen,because each upload depends on values returned by previous upload.
Please let me know how it can be achieved. Note : $scope.upload is part angular file upload js ()
$scope.startUploading = function ($files, errFiles, item) {
//$files: an array of files selected
for (var idx = 0; idx < $files.length; idx++) {
var $file = $files[idx];
(function (index) {
$scope.upload[index] = $upload.upload({
url: "../UploadCommentAttachment",
method: "POST",
file: $file,
data: item
}).success(function (responseData, status, headers, config) {
item.AuditCommentAttachments.push(responseData);
item.CommentId = responseData.CommentId;
item.AuditId = responseData.AuditId;
item.Operation = "EDIT";
item.CommentAttachmentID = responseData.CommentAttachmentID;
}
});
})(idx);
} ///-- for loop end
} // startuploading end
Below is the code I use in my project to allow user to upload file(s). For each file selected , "$scope.upload" function gets invoked asynchronously.
Because these are asynchronous calls , each call does not depend on previous call.
But my requirement is only after success call back function gets executed, then the next upload should happen,because each upload depends on values returned by previous upload.
Please let me know how it can be achieved. Note : $scope.upload is part angular file upload js (https://github./danialfarid/ng-file-upload)
$scope.startUploading = function ($files, errFiles, item) {
//$files: an array of files selected
for (var idx = 0; idx < $files.length; idx++) {
var $file = $files[idx];
(function (index) {
$scope.upload[index] = $upload.upload({
url: "../UploadCommentAttachment",
method: "POST",
file: $file,
data: item
}).success(function (responseData, status, headers, config) {
item.AuditCommentAttachments.push(responseData);
item.CommentId = responseData.CommentId;
item.AuditId = responseData.AuditId;
item.Operation = "EDIT";
item.CommentAttachmentID = responseData.CommentAttachmentID;
}
});
})(idx);
} ///-- for loop end
} // startuploading end
Share
Improve this question
asked Jan 23, 2016 at 4:02
refactorrefactor
15.2k25 gold badges72 silver badges105 bronze badges
1
- Solution provided here is elegant and works for me. – refactor Commented Jan 27, 2016 at 11:22
3 Answers
Reset to default 4Generic Answer
The way to sequentially execute asynchronous operations is to chain them using the promises derived by .then
methods. Values to be used by the subsequent promises should be returned inside the .then
method.
function sequentiallyExecute = function (opList) {
//create initial object
var passedObject = {};
passedObject.dataList = [];
passedObject.response = {};
//create initial promise
var promise = $q.when(passedObject);
//sequentially execute opList
angular.forEach(opList, function(op) {
promise.then(function (passedObject) {
var iPromise = asyncOperation(op, passedObject);
iPromise.then(function onFulfulled(response) {
//save response for chaining
passedObject.response = response;
//push data to list
passedObject.dataList.push(response.data);
//return object for chaining
return passedObject;
});
//return promise for chaining
return iPromise;
});
});
//return promise to client
return promise;
};
In client code, retrieve final data.
(sequentiallyExecute(opList)
).then (function onFulfilled(passedObject) {
$scope.dataList = passedObject.dataList;
}).catch (function onRejected(error) {
console.error(error);
});
For more information on chaining, see the AngularJS $q Service API Reference -- chaining promises.
Specific Answer
Instead of using the .success
method (which is not chainable), use the .then
method (which is chainable).
function asychOperation(op, passedObject) {
var $file = op.$file
var item = passedObject.item;
var httpPromise = $upload.upload({
url: "../UploadCommentAttachment",
method: "POST",
file: $file,
data: item
});
var derivedPromise = httpPromise.then(function (response) {
var data = response.data
passedObject.dataList.push(data);
var newItem = {};
newItem.CommentId = data.CommentId;
newItem.AuditId = data.AuditId;
newItem.Operation = "EDIT";
newItem.CommentAttachmentID = data.CommentAttachmentID;
passedObject.item = newItem;
});
var promise = derivedPromise.then (function onFulfilled() {
//return object for chaining
return passedObject;
});
//return promise for chaining
return promise;
};
The
.success
and.error
methods of the$http
service have been removed from the AngularJS framework. Instead use the.then
and.catch
methods.For more information, see
- Why are AngularJS $http success/error methods deprecated? Removed from v1.6?
Conclusion
Because calling the .then
method of a promise returns a new derived promise, it is easily possible to create a chain of promises.
It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.1
Below code does the required job for me , sequentially execute asynchronous operations , without chaining.
$scope.startUploading = function ($files, item)
{
var cntr = 0;
function next()
{
if (cntr < $files.length)
{
$scope.upload[cntr] = $upload.upload({
url: "../UploadCommentAttachment",
method: "POST",
file: $files[cntr],
data: item
}).then(function (responseData) { item.AuditCommentAttachments.push(responseData.data);
item.CommentId = responseData.data.CommentId;
item.AuditId = responseData.data.AuditId;
item.Operation = "EDIT";
item.CommentAttachmentID = responseData.data.CommentAttachmentID;
cntr++;
next();
});
}
}
next();
}
I have modified an fiddle from the ng-file-upload to handle posts one by one and also use the responce from the previous post to the current one. Please check my fiddle
Here is the part of javascript code that does the trick
function sequentialFileUploadWithReduce(values) {
var result = ''
var dfd = $q.defer();
dfd.resolve();
return values.reduce(function(currentValue, nextValue, index, values) {
return Upload.upload({
url: "https://angular-file-upload-cors-srv.appspot./upload",
method: "POST",
data: currentValue
}).then(function(resp) {
// file is uploaded successfully
//debugger;
result += "Uploading file " + index++;
console.log("Uploading file " + index);
nextValue.item = resp;
}, function(resp) {
// handle error
}, function(evt) {
// progress notify
});
}, dfd.promise).then(function(responce) {
return result;
});
}
Hope this helps. Also look at the console and network tab to verify that the solution is working.