2014-04-14 9 views
5

Ho intenzione di caricare un array di file usando jQuery.Come utilizzare quando inviare il file in modo sequenziale in una funzione che è anche una promessa differita?

Questa intenzione è racchiusa in una funzione denominata uploadFilesUsingAjax();

var uploadFilesPromise = uploadFilesUsingAjax(); 

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

Ho bisogno di aspettare che tutti i file vengano caricati correttamente prima di fare qualcos'altro.

All'interno uploadFilesUsingAjax(),

ho scritto il mio codice in questo modo

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!')}); 

Purtroppo, non ero in grado di caricare un sacco di file prima mi sono reindirizzato alla pagina di successo.

Ho provato varie soluzioni StackOverflow su come eseguire questa operazione. Finora niente funziona. Si prega di avvisare.

Alcuni codici sono stati troncati per risparmiare spazio.

risposta

2

Tutte le tue promesse sono parallele e non sequenziali.

Una promessa rappresenta un'attività già in esecuzione. Le promesse in JavaScript, a differenza delle attività C# o di altre astrazioni sono già state avviate. Il modo di rappresentare un'attività che non è stata avviata è una funzione che restituisce una promessa.

Dal promises[i] è già una promessa - quando si fa promise.then(object) non aggiunge un gestore .then, ma piuttosto restituire immediatamente. .then ignora tutti gli argomenti che non sono una funzione.

Questo è il motivo per cui ritorna presto, ritorna non appena la prima promessa soddisfa. Inoltre, non è necessario il .when. Creare una funzione che crea un processo di caricamento in quanto tale:

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 
    } 
} 

Ora, è possibile mappare i file in compiti:

var tasks = files.map(createUploadTask); 

nota, che ora i compiti sono ogni funzioni che restituiscono una promessa su un caricamento di file. Non sono promesse.

Ora, è possibile concatenare:

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; 

È anche ora non è necessario utilizzare quando, dal momento che si restituisce un singolo promessa . Presumo che tu non abbia bisogno del risultato attuale qui (ma solo per conoscere il successo/fallimento).

È sufficiente fare:

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

Ho un piccolo problema con il '.map' perché l'URL può essere diversa a seconda di ciò che voglio usare l'uploadFilesUsingAjax(). Come posso superare questa situazione? –

+0

Si sta mappando un array di _stuff_ in una matrice di attività di richiesta. Hai accesso all'oggetto file - puoi '.map' a un URL diverso basato su una proprietà dell'oggetto' file' passato alla funzione you' .map' con. –

+0

Mi dispiace per il commento precedente. So come risolvere il problema. Ho invece scritto funzioni alias. Ho provato ad aggiungere una parentesi in più che mancava nella risposta. BUt non poteva perché StackOverflow richiede di aggiungere almeno 6 caratteri. Mancano 'd.reject (new Error (" Chiamato uploadFiles senza file da caricare "));' Dovrebbe essere una doppia parentesi alla fine. –

Problemi correlati