Reputation: 15044
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.com/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
Upvotes: 1
Views: 3505
Reputation: 48948
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.
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
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
Upvotes: 4
Reputation: 15044
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();
}
Upvotes: 2
Reputation: 328
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.com/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.
Upvotes: 1