I have an array of images which I iterate and upload each to a remote server. Once the file is uploaded I use the result to get the file name and push to another array.
The problem I'm having is that not all results are being pushed to the array. I'm trying to construct an array with the results for all images uploaded. The following code executes lastTask
before the forEach
loop is finished:
/**
* Upload picture
*/
$scope.upload = function () {
var token = window.localStorage.getItem('yourTokenKey');
var defer = $q.defer();
var promises = [];
var options = {
fileKey: "image",
fileName: " test.png",
chunkedMode: true,
mimeType: "image/png",
headers: {'x-auth-token': token}
};
function lastTask() {
alert(promises);
$scope.images = [];
$scope.hide();
$scope.showAlert();
$scope.putQc();
alert('finish').then(function () {
defer.resolve();
});
}
angular.forEach($scope.images, function (image) {
$cordovaFileTransfer.upload("http://server.com/file/upload", image, options).then(function (result) {
// Success!
promises.push(result.response + '.jpg');
alert(JSON.stringify(result));
console.log("SUCCESS: " + JSON.stringify(result.response));
// Error
}, function (err) {
alert(JSON.stringify(err));
console.log("ERROR: " + JSON.stringify(err));
// constant progress updates
}, function (progress) {
$scope.show();
});
});
$q.all(promises).then(lastTask);
return defer;
};
If I am correct, your promises
array is the problem, instead of pushing the promises coming from $cordovaFileTransfer.upload
you are pushing the result string that you are getting.
Second issue, I think what you are doing is promise anti-pattern, no need for $q.defer()
here.
(third, might be irrelevent, JSON.stringify
not really needed, and hoping that alert
is defined by you)
I would re-write it as:
/**
* Upload picture
*/
$scope.upload = function () {
var token = window.localStorage.getItem('yourTokenKey');
var promises = [], results; //CHANGED
var options = {
fileKey: "image",
fileName: " test.png",
chunkedMode: true,
mimeType: "image/png",
headers: {'x-auth-token': token}
};
function lastTask() {
alert(results); //CHANGED
$scope.images = [];
$scope.hide();
$scope.showAlert();
$scope.putQc();
return alert('finish'); //CHANGED
}
promises = $scope.images.map(function (image) { //CHANGED
return $cordovaFileTransfer.upload("http://server.com/file/upload", image, options).then(function (result) { //CHANGED
// Success!
results.push(result.response + '.jpg'); //CHANGED
alert(JSON.stringify(result));
console.log("SUCCESS: " + JSON.stringify(result.response));
// Error
}, function (err) {
alert(JSON.stringify(err));
console.log("ERROR: " + JSON.stringify(err));
throw err;
// constant progress updates
}, function (progress) {
$scope.show();
});
});
return $q.all(promises).then(lastTask); //CHANGED
};
The problem is that you are not pushing promises into your promise array. You are (eventually) pushing normal values into the array, but not before you've run a $q.all
on an empty or near-empty array.
$scope.upload = function() {
var token = window.localStorage.getItem('yourTokenKey');
var promises = [];
var options = {
fileKey: "image",
fileName: " test.png",
chunkedMode: true,
mimeType: "image/png",
headers: {
'x-auth-token': token
}
};
function lastTask(results) {
alert(results);
$scope.images = [];
$scope.hide();
$scope.showAlert();
$scope.putQc();
return results; // keeping the promise chain useful
}
angular.forEach($scope.images, function(image) {
var promiseForUpload = $cordovaFileTransfer.upload("http://server.com/file/upload", image, options).then(function(result) {
console.log("SUCCESS: " + JSON.stringify(result.response));
return result.response + '.jpg'; // this will be the promised value
}, function(err) {
console.log("ERROR: " + JSON.stringify(err));
throw err; // re-throwing the error so the q.all promise is rejected
}, function(progress) {
$scope.show();
});
promises.push(promiseForUpload);
});
return $q.all(promises).then(lastTask); // returns a promise for results
};
Honestly though I would get rid of the mid-level stuff altogether:
$scope.upload = function() {
var token = window.localStorage.getItem('yourTokenKey');
var options = { /* stuff */ };
function lastTask(results) { // I wouldn't bake this into the $scope.upload, but do it as a result of calling $scope.upload (i.e. with .then).
alert(results);
$scope.images = [];
$scope.hide();
$scope.showAlert();
$scope.putQc();
return results; // keeping the promise chain useful
}
var promises = $scope.images.map(function(image){
return $cordovaFileTransfer.upload("http://server.com/file/upload", image, options);
});
return $q.all(promises).then(lastTask); // returns a promise for results
};
Finally, if this function is supposed to return a promise, then wherever it is used be sure to add error handling (e.g. with .catch
at the end of the chain). If this function is supposed to be the end of the promise chain (e.g. triggered just by a button click and nothing is listening to the response), then you need to add the catch to the end of $q.all(promises).then(lastTask)
. Don't let silent errors occur.