I have a requirement which requires chaining of Promises. In my Ionic app, I need to iterate over a list of files and zip them. Then the zip needs to be stored on the device itself (iPhone in this case).
I already have the list of files that need to be zipped in an array. So, I am iterating over them and using $cordovaFile getting the binay content of these files. Then I am adding binary to a JSZip object. End result should be that binary content of all the files should be added to zip.file, so that a zip file can be generated.
//wrapping in Promise.all so that we don't proceed until we have the content of all files added to zip
var zip = new JSZip();
return Promise.all(
filesList.forEach(function(file) {
console.log('file to be added using $cordovaFile '+file);
// Getting the content of each file using $cordovaFile. This returns a promise.
return $cordovaFile.readAsBinaryString(cordova.file.dataDirectory + $rootScope.username, file)
.then(function(binaryData) {
return new Promise(function(resolve, reject) {
//Adding content of all files to zip.file so that it can be zipped in the next step.
resolve(zip.file(file, binaryData, {binary: true}));
})
})
.catch(function(error) {
console.log('Error during fetch content or zipping '+JSON.stringify(error));
})
})
)
Once zip.file has all the contents, I am calling another function in JSZip which would generate the zip. This would return a promise as well, so I need to chain to $cordovaFile.writeFile, so that the zip can be written locally. $cordovaFile.writeFile also returns a Promise which would the last promise in the chain.
.then(function(zipData) {
// async request to generate the zip
return zipData.generateAsync({type:"blob"});
}).then(function (blob) {
// once we have the zip, save it to the device
$cordovaFile.writeFile(cordova.file.dataDirectory+$rootScope.username, 'abc.zip', blob, true)
.then(function(data) {
console.log('Zip file written to device at '+cordova.file.dataDirectory+$rootScope.username);
})
}).catch(function(error) {
console.log('Error while zipping and writing '+JSON.stringify(error));
})
This is how the complete code looks like
var zipFiles = function(filesList) {
var zip = new JSZip();
return Promise.all(
filesList.forEach(function(file) {
return $cordovaFile.readAsBinaryString(cordova.file.dataDirectory + $rootScope.username, file)
.then(function(binaryData) {
return new Promise(function(resolve, reject) {
resolve(zip.file(file, binaryData, {binary: true}));
})
})
.catch(function(error) {
console.log('Error during fetch content or zipping '+JSON.stringify(error));
})
})
)
.then(function(zipData) {
return zipData.generateAsync({type:"blob"});
}).then(function (blob) {
$cordovaFile.writeFile(cordova.file.dataDirectory+$rootScope.username, 'abc.zip', blob, true)
.then(function(data) {
console.log('Zip file written to device at '+cordova.file.dataDirectory+$rootScope.username);
})
}).catch(function(error) {
console.log('Error while zipping and writing '+JSON.stringify(error));
})
}
Challenge is that after Promise.all completes, nothing gets executed. So, nothing starting 'then(function(zipData)' gets executed.
I feel it has something to do with the way I am chaining Promises. Any help will be highly appreciated.
This is because forEach
returns undefined
, thus Promise.all
resolves immediately. You should change that to .map
.
Moreover, keep in mind that your zipData
argument would not be what you expect. This promise's arguments will contain every result returned from zip.file(file, binaryData, {binary: true})
.
In this case you do not need the zipData
. The zip
variable will do the job. In the code bellow I have also simplified the promises chain by removing a redundant promise in the loop and taking one .then
outside.
var zipFiles = function (filesList) {
var zip = new JSZip();
var zipFilesPromises = filesList.map(function (file) {
return $cordovaFile.readAsBinaryString(cordova.file.dataDirectory + $rootScope.username, file)
.then(function (binaryData) {
return zip.file(file, binaryData, { binary: true });
});
});
return Promise.all(zipFilesPromises)
.then(function () {
return zip.generateAsync({ type: "blob" });
})
.then(function (blob) {
return $cordovaFile.writeFile(cordova.file.dataDirectory + $rootScope.username, 'abc.zip', blob, true);
})
.then(function (data) {
console.log('Zip file written to device at ' + cordova.file.dataDirectory + $rootScope.username);
})
.catch(function (error) {
console.log('Error while zipping and writing ' + JSON.stringify(error));
})
}