2014-04-14 10 views
5

Zamierzam załadować tablicę plików za pomocą jQuery.Jak używać, kiedy należy przesłać dalej plik w funkcji, która jest również odroczoną obietnicą?

Ten zamiar jest zawijany w funkcję o nazwie uploadFilesUsingAjax();

var uploadFilesPromise = uploadFilesUsingAjax(); 

$.when(uploadFilesPromise).done(function (uploadFilesAjaxResult) { 
     // redirect to success page... 

Muszę poczekać, aż wszystkie pliki zostaną pomyślnie przesłane, zanim zrobię coś innego.

Wewnątrz uploadFilesUsingAjax(),

pisałem mój kod ten sposób

function uploadFilesUsingAjax() { 
    var files = pages; // pages is a global variable which is an array of files 
    var url = "https://stackoverflow.com/users/" + currentUser.id + "/files.json"; 
    var type = "POST"; 

    console.info('files length:' + files.length); 
    if (files.length > 0) { 

     var promises=[]; 
     for (var i = 0; i < files.length; i++) { 
      var data = new FormData(); 
      var postData = {}; 
      var file = files.getByIndex(i); 
      var key = i + 1; 
      if (typeof (file.id) !== "undefined" && file.id > 0) { 
       data.append(key, JSON.stringify(file)); 
      } else { 
       data.append(key, file); 
      } 
      var request = $.ajax({ 
       //this is the php file that processes the data 
       url: url, 

       //POST method is used 
       type: type, 

       //pass the data 
       data: data, 

       //Do not cache the page 
       cache: false, 

       xhr: function() { 
        // custom xhr 
        myXhr = $.ajaxSettings.xhr(); 
        if(myXhr.upload) { // check if upload property exists 

          myXhr.upload.addEventListener('progress',updatePagesProgress, false); // for handling the progress of the upload 
        } 
        return myXhr; 
       }, 

       // DO NOT set the contentType and processData 
       // see http://stackoverflow.com/a/5976031/80353 
       contentType: false, 
       processData: false, 

       //success 
       success: function (json) { 
        // json is already an object thanks to cakephp code to serialize 

        //if POST is a success expect no errors 
        if (json.error == null && json.result != null) { 
         currentUser = json.result.User; 
        // error 
        } else { 
         alert(json.error); 
        } 
       } 
      }); 
      promises.push(request); 
     } 

     var promise = promises[0]; 
     for (var i = 1; i < promises.length; i++) { 
      promise = promise.then(promises[i]); 
     } 

     return promise.done(function() { console.log('all!')}); 

Niestety, nie udało się załadować wiele plików przed mam przekierowany do strony sukces.

Próbowałem różnych rozwiązań StackOverflow, jak to zrobić. Do tej pory nic nie działa. Proszę doradź.

Niektóre kody zostały obcięte, aby zaoszczędzić miejsce.

Odpowiedz

2

Wszystkie twoje obietnice są równoległe, a nie sekwencyjne.

Obietnica reprezentuje zadanie już działające. Obietnice w JavaScript, w przeciwieństwie do zadań C# i innych abstrakcji już się rozpoczęły. Sposób reprezentowania zadania, które nie zostało uruchomione, jest funkcją zwracającą obietnicę.

Od promises[i] jest już obietnicą - jeśli wykonasz promise.then(object), to nie doda ona .then obsługi, a raczej natychmiast zwróci. .then ignoruje wszelkie argumenty, które nie są funkcją.

Dlatego powraca wcześniej, powraca, gdy tylko spełni się pierwsza obietnica. Nie potrzebujesz również .when. Utworzyć funkcję, która tworzy proces wysyłania jako takie:

function createUploadTask(file,i){ 
    return function(){ 
     var data = new FormData(); 
     var postData = {}; 
     var file = files.getByIndex(i); 
     var key = i + 1; 
     if (typeof (file.id) !== "undefined" && file.id > 0) { 
      data.append(key, JSON.stringify(file)); 
     } else { 
      data.append(key, file); 
     } 
     return $.ajax({...}); // return the promise 
    } 
} 

Teraz można mapować pliki do zadania:

var tasks = files.map(createUploadTask); 

uwaga, że ​​teraz zadania są każdy funkcje zwracające obietnicę nad przesłaniem pliku. Nie są obietnicami.

Teraz można je Łańcuch:

var p = tasks[0](); // start off the chain 
for(var i = 1; i < tasks.length; i++){ 
     // chain the next task, note, that we're passing a _function_ here 
     // when you return a promise from a `.then` it will fulfill when that promise 
     // fulfills, in our case the $.ajax 
     p = p.then(tasks[i]); 
} 
return p; 

też teraz nie trzeba używać kiedy, ponieważ zwracają pojedynczy obietnicy. Zakładam, że nie potrzebujesz tutaj rzeczywistego wyniku (ale tylko znać sukces/porażkę).

po prostu zrobić:

function uploadFilesUsingAjax() { 
    // settings 
    if(tasks.length === 0){ 
      // a method MUST always either return either synchronously or asynchronously never 
      // both, always return a promise. Otherwise you get API hell. 
      var d = $.Deferred(); 
      d.reject(new Error("Called uploadFiles with no files to upload")); 
      return d.promise; 
    } 
    tasks = pages.map(createUploadTask) 
    var p = tasks[0](); // call first one 
    for(var i = 1; i < tasks.length; i++) p = p.then(tasks[i]); 
    return p; 
} 
+0

Mam mały problem przy użyciu '.map' ponieważ URL może być różny w zależności od tego, co chcę użyć uploadFilesUsingAjax(). Jak mogę przezwyciężyć tę sytuację? –

+0

Odwzorowujesz tablicę _stuff_ na tablicę zadań żądań. Masz dostęp do obiektu pliku - możesz '.map' na inny adres URL na podstawie właściwości obiektu' file' przekazanego do funkcji którą '.map' z. –

+0

Przepraszamy za wcześniejszy komentarz. Wiem, jak obejść ten problem. Zamiast tego napisałem funkcje aliasów. Próbowałem dodać dodatkowy nawias, którego brakowało w twojej odpowiedzi. BUt nie mógł, ponieważ StackOverflow wymaga dodania co najmniej 6 znaków. Brakuje ci w 'd.reject (nowy błąd (" Called uploadFiles bez plików do przesłania "));' Powinien być podwójny nawias na końcu. –

Powiązane problemy