1

I'm using ngCordova File Transfer plugin in an ionic project to download set of images from urls. Here is the code i'm using for that.

// Save a image file in a given directory
$scope.saveImage = function(dir,imgUrl,imageName) {

    var url = imgUrl;
    var targetPath = cordova.file.dataDirectory+ dir+"/" + imageName;
    var trustHosts = true;
    var options = {};

    // Download the image using cordovafiletransfer plugin
    $cordovaFileTransfer.download(url, targetPath, options, trustHosts)
    .then(function(result) {
        $scope.loadedCount ++;
        $ionicLoading.show({template : "<ion-spinner class='spinner-energized'></ion-spinner><p> Downloading pages : "+ $scope.loadedCount+" of "+ $scope.pages.length+ "</p><p>Please wait...</p><p><button class=\"button button-block button-positive\">continue in background</button></p>"});
        if($scope.loadedCount == $scope.pages.length) {
            $ionicLoading.hide();
            $scope.showDownloadSuccessAlert = function() {
                var alertPopup = $ionicPopup.alert({
                    title: 'Success!',
                    template: "Your magazine successfully downloaded. You can view it on Downloads!"
                });
            };
            $scope.showDownloadSuccessAlert();
        }
    }, function(err) {
        alert(JSON.stringify(err));
    }, function (progress) {
        if($scope.loadedCount > 80) {
        }
    });
};

// Download the current magazine
$scope.downloadMagazine = function() {
    if($rootScope.user.user_id == undefined) {
        $scope.showLoginAlert = function() {
                var alertPopup = $ionicPopup.alert({
                    title: 'Oops!',
                    template: "Your must login to download magazines"
                });
            };
        $scope.showLoginAlert();
        return;
    }

    document.addEventListener('deviceready', function () {

        var dirName = $rootScope.currentIssue.slug+'_VOL_'+$rootScope.currentIssue.vol+'_ISU_'+$rootScope.currentIssue.issue;

        // First create the directory
         $cordovaFile.createDir(cordova.file.dataDirectory, dirName, false)
        .then(function (success) {
            var count = 1;
            $scope.loadedCount = 0;
            angular.forEach($scope.pages, function(value, key) {
               var imgName = count+".png";
               $scope.saveImage(dirName,value.link,imgName); // Then save images one by one to the created directory.
               count++;
            });

        }, function (error) {
            // Directory already exists means that the magazine is already downloaded.
            $scope.showDownloadedAlert = function() {
                var alertPopup = $ionicPopup.alert({
                    title: 'Why worry!',
                    template: "Your have already downloaded this magazine. You can view it on downloads"
                });
            };
            $scope.showDownloadedAlert();
        });

    }, false);
};

})

Problem here is that program try to download everything at once without waiting for previous one to finish. How to wait for one file to finish downloading and then start the other?

Thanks

2 Answers2

1

If you want to do that automatically (you're not the first one : How can I execute array of promises in sequential order?), you could try reducing the list of address to a single Promise that will do the whole chain.

$scope.pages.reduce(function (curr,next) {
      return curr.then(function(){            
            return $scope.saveImage(dirName, curr.link, imgName);
      });
  }, Promise.resolve()).then(function(result) {

        $ionicLoading.show({template : "<ion-spinner class='spinner-energized'></ion-spinner><p> Downloading pages : "+ $scope.loadedCount+" of "+ $scope.pages.length+ "</p><p>Please wait...</p><p><button class=\"button button-block button-positive\">continue in background</button></p>"});
        if($scope.loadedCount == $scope.pages.length) {
            $ionicLoading.hide();
            $scope.showDownloadSuccessAlert = function() {
                var alertPopup = $ionicPopup.alert({
                title: 'Success!',
                template: "Your magazine successfully downloaded. You can view it on Downloads!"
            });
        };
        $scope.showDownloadSuccessAlert();
    }
  });

And don't forget to make your saveImage async which returns a promise.

UPDATE:

You will need to remove the then logic from your save method and return the download method call:

return $cordovaFileTransfer.download(url, targetPath, options, trustHosts).promise;

Then you can put your download handler into Promise.resolve()).then. See above.

Community
  • 1
  • 1
Yaser
  • 5,609
  • 1
  • 15
  • 27
0

There's no other way other than chaining your promises. Here's an example:

angular.module('app', [])
  .service('fakeDownloadService', function($timeout) {
    this.download = (file) => $timeout(() => file, 100);
    return this;
  })
  .run(function($rootScope, $q, fakeDownloadService) {
    var listOfFiles = [];
    for (var i = 0; i < 10; i++)
      listOfFiles.push('file' + i);
    $rootScope.log = [];
    $rootScope.download = () => {
      listOfFiles
        .reduce((prev, curr) => {
          return prev.then((result) => {
            if(result)
            $rootScope.log.push(result + ' downloaded');
            return fakeDownloadService.download(curr);
          });
        }, $q.resolve())
        .then(() => $rootScope.log.push('all done'));
    };
  });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.9/angular.min.js"></script>
<div ng-app="app">
  <button ng-click="download()">Start</button>
  <div>Log:</div>
  <ul>
    <li ng-repeat="entry in log track by $index">
      {{entry}}
    </li>
  </ul>
</div>
Muli Yulzary
  • 2,559
  • 3
  • 21
  • 39