kayasa kayasa - 2 months ago 18
AngularJS Question

Chaining multiple promises, including Promise.all

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.

Answer

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