2016-09-16 3 views
0

Я здание ионного проекта, где пользователи могут играть тур (которого данные из API)Проблем с угловым Еогеаспом ждет HTTP вызывают

Каждого тура имеет некоторое количество частей, которые могут играть пользователи в определенную точку на карте. Это приложение должно быть 100% -ным офлайн-приложением, поэтому, когда пользователь вводит код тура, данные должны быть извлечены из API до того, как пользователь сможет продолжить (так что приложение поместит все данные тура в автономном режиме). Каждая часть имеет изображение, видео, аудио, которое загружается в начале приложения.

Проблема в том, что вызов функции, который загружает все данные, не является синхронным. В console.log говорится, что функция уже заканчивается перед загрузкой всех данных. Куски кода ниже:

 function getAndFillFullTour() { 
    vm.showLoader = true; 
    // load data 
    TourFactory.getFullTour(vm.tourData.number, function(data){ 
     if(data.state == 'success'){ 
     vm.tourData = data; 
     var test = downloadData(function(){ 
      // hide loader and continue tour 
     }); 
     } else { 
     console.log('error'); 
     } 
    }); 
    } 

Эта функция вызывает завод, который получает полный тур, включая пути изображений каждой части, которая необходима, чтобы загрузить на устройство пользователей. Функция downloadData является следующей функцией:

function downloadData(callback) { 
    angular.forEach(vm.tourData.parts, function(value, key){ 
     var part = value; 
     var i = key; 

     if(part.image !== "") { 
     TourFactory.getPartImage(part, tourId, function(data){ 
      vm.tourData.parts[i].partImage = data; 
      console.log('executed with picture ' + i); 
     }); 
     } 

    }); 

    if(callback) 
     callback(); 
    } 

К сожалению, forloop сам выполняет синхронным, но не ждет завод вызова завершить. Я попробовал много альтернатив с обещаниями, но без везения. Может ли кто-нибудь помочь? Мне нужно дождаться завершения http-вызова, чтобы получить ответ от вызова downloadData.

getPartImage() - всего лишь пример, для каждого цикла есть пять таких функций, которые должны быть заполнены первым, прежде чем я получу ответ в вызове downloadData.

+0

['forEach' ничего не ждет] (http://stackoverflow.com/a/37576787/1048572). – Bergi

+0

Пожалуйста, покажите нам свои попытки использовать обещания. – Bergi

ответ

1

Посмотрите на $q.all или here - это вспомогательная функция обещания, которая может подождать нескольких обещаний. Это результат и обещание, поэтому вы можете связать его с другими обещаниями.

// Promise function that knows how to download a single part 
function downloadPart(myurl) { 
    // return http promise 
    return $http({ 
    method: 'GET', 
    url: myurl 
    }); 
}; 

// Aggregat epromise that downloads all parts 
function downloadAllParts(parts) { 
    var defer = $q.defer(); // Setup return promise 
    var partsPromises = []; // Setup array for indivudual part promises 
    angular.forEach(parts, function(part) { // Iterate through each part 
    // Schedule download of a single 
    partsPromises.push(downloadPart(part)); 
    }); 
    // Wait for all parts to resolve 
    $q.all(partsPromises) 
    .then(function(data) { 
     // Returned data will be an array of results from each individual http promise 
     resData = []; 
     angular.forEach(data, function(partData) { 
     //handle each return part 
     resData.push(partData.data); 
     }) 
     defer.resolve(resData); // Notify client we downloaded all parts 
    }, function error(response) { // Handle possible errors 
     console.log('Error while downloading parts' 
     response); 
     defer.reject('Error while downloading parts'); 
    }); 
    return defer.promise; 
}; 

Затем в вашем клиенте, вы можете просто ждать downloadAllParts для завершения:

downloadAllParts(myParts) 
.then(function(data) { 
    alert('Success!'); 
}, function(error) { 
    alert(error); 
}) 

С $ q.all это обещание, а также, вы можете избавиться от Задерживает все вместе:

// Aggregat epromise that downloads all parts 
function downloadAllParts(parts) { 
    var partsPromises = []; // Setup array for indivudual part promises 
    angular.forEach(parts, function(part) { // Iterate through each part 
    // Schedule download of a single 
    partsPromises.push(downloadPart(part)); 
    }); 
    // Wait for all parts to resolve 
    return $q.all(partsPromises) 
    .then(function(data) { 
     // Returned data will be an array of results from each individual http promise 
     var resData = []; 
     angular.forEach(data, function(partData) { 
     //handle each return part 
     resData.push(partData.data); 
     }) 
     return resData; 
    }); 
}; 

Вот рабочая jsfiddle: link

+0

нет необходимости в '$ q.defer()' ... просто вернуть '$ q.all()', но обязательно возвращать данные внутри 'then()' – charlietfl

+0

также необходимо получить данные из каждого 'downloadPart () 'response object – charlietfl

+0

downloadPart возвращает обещание, которое возвращает данные, поэтому« данные »в $ q.all обещание должно быть массивом результатов из всех вызовов $ http. – Maksym

0

Спасибо всем! Следующий код работал для меня. Я объединил решения с комментариями с некоторыми вещами, и это решение заставило его работать на меня.

// Aggregat epromise that downloads all parts 
    function downloadAllParts(parts) { 
    vm.showLoader = true; 
    var defer = $q.defer(); // Setup return promise 
    var partsPromises = []; // Setup array for indivudual part promises 
    angular.forEach(parts, function(part, key) { // Iterate through each part 
     // Schedule download of a single 
     if(typeof part.image !== 'undefined' && part.image !== "") { 
     partsPromises.push(downloadPartImage(part)); 
     } 

     if(typeof part.audio !== 'undefined' && part.audio !== "") { 
     partsPromises.push(downloadPartAudio(part)); 
     } 

     if(typeof part.video !== 'undefined' && part.video !== "") { 
     partsPromises.push(downloadPartVideo(part)); 
     } 

     if(key > 0){ 
     vm.tourData.parts[key].available = false; 
     } else { 
     vm.tourData.parts[key].available = true; 
     } 
    }); 
    // Wait for all parts to resolve 
    $q.all(partsPromises) 
     .then(function(data) { 
     // Returned data will be an array of results from each individual http promise 
     resData = []; 
     angular.forEach(data, function(partData) { 
      //handle each return part 
      resData.push(partData); 
     }) 
     defer.resolve(resData); // Notify client we downloaded all parts 
     }, function error(response) { // Handle possible errors 
     console.log('Error while downloading parts' + response); 
     defer.reject('Error while downloading parts'); 
     }); 
    return defer.promise; 
    } 

    function downloadPartImage(part) { 
    var data = { 
     oid: tourId, 
     plid: part.image, 
     func: 'image' 
    }; 

    return TourFactory.getSynchronousPartImage(part, tourId).then(function(data){ 
     part.partImage = data.data; 
     return data; 
    }); 
    }; 

    function downloadPartAudio(part) { 
    var targetPath = cordova.file.externalDataDirectory + tourId + '/audio/' + part._id.$id + '.mp3'; 
    var url = "https://www.tourtodo.com/gameinfo/" + part.audio; 
    var trustHosts = true; 
    var options = {}; 

    return $cordovaFileTransfer.download(url, targetPath, {}, true).then(function (result) { 
     console.log('Save file on '+targetPath+' success!'); 
     part.audioSrc = targetPath; 
     return result; 
    }, function (error) { 
     console.log('Error Download file'); 
     console.log(JSON.stringify(error)); 
     return error; 
    }, function (progress) { 
     console.log((progress.loaded/progress.total) * 100); 
    }); 
    } 

    function downloadPartVideo(part) { 
    var targetPath = cordova.file.externalDataDirectory + tourId + '/video/' + part._id.$id + '.mp4'; 
    var url = "https://www.tourtodo.com/gameinfo/" + part.video; 
    var trustHosts = true; 
    var options = {}; 

    return $cordovaFileTransfer.download(url, targetPath, {}, true).then(function (result) { 
     console.log('Save file on '+targetPath+' success!'); 
     part.videoSrc = targetPath; 
     return result; 
    }, function (error) { 
     console.log('Error Download file'); 
     console.log(JSON.stringify(error)); 
     return error; 
    }, function (progress) { 
     console.log((progress.loaded/progress.total) * 100); 
    }); 
    } 

    function getAndFillFullTour() { 
    vm.showLoader = true; 
    // load data 
    TourFactory.getFullTour(vm.tourData.number, function(data){ 
     if(data.state == 'success'){ 
     vm.tourData = data; 

     downloadAllParts(vm.tourData.parts) 
     .then(function(data) { 
      vm.showLoader = false; 
      vm.showStartButton = true; 
      alertPopup = $ionicPopup.alert({ 
       title: 'Gelukt!', 
       template: 'De tour is opgehaald. Druk op start om de tour te starten.' 
      }); 
      localStorage.setItem('tourdata', JSON.stringify(vm.tourData)); 
      console.log(JSON.parse(localStorage.getItem('tourdata'))); 
     }, function(error) { 
      console.log('error'); 
      console.log(error); 
     }) 
     } else { 
     console.log('error'); 
     } 
    }); 
    } 
+0

Luuk, вы можете принять свое решение, если это то, что сработало для вас. В противном случае вопрос будет задерживаться в очереди «Открытые вопросы». Ура! – Maksym