Como usar o quando então enviar upload de arquivos sequencialmente em uma função que também é uma promessa diferida?

StackOverflow https://stackoverflow.com//questions/23065705

Pergunta

Pretendo fazer upload de uma série de arquivos usando jQuery.

Esta intenção está envolvida em uma função chamada uploadFilesUsingAjax();

var uploadFilesPromise = uploadFilesUsingAjax();

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

Preciso esperar que todos os arquivos sejam carregados com sucesso antes de fazer outra coisa.

Dentro uploadFilesUsingAjax(),

Eu escrevi meu código dessa maneira

function uploadFilesUsingAjax() {
    var files = pages; // pages is a global variable which is an array of files
    var url = "/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!')});

Infelizmente, não consegui fazer upload de muitos arquivos antes de ser redirecionado para a página de sucesso.

Eu tentei várias soluções StackOverflow sobre como fazer isso.Até agora nada funciona.Por favor, avise.

Algum código foi truncado para economizar espaço.

Foi útil?

Solução

Todas as suas promessas são paralelas e não sequenciais.

Uma promessa representa um já correndo tarefa.Promessas em JavaScript, ao contrário de tarefas C# ou outras abstrações, já foram iniciadas.A forma de representar uma tarefa que não foi iniciada é uma função que retorna uma promessa.

Desde promises[i] já é uma promessa - quando você fizer isso promise.then(object) ele não adiciona um manipulador .then, mas retorna imediatamente. .then ignora quaisquer argumentos que não sejam uma função.

É por isso que retorna cedo, retorna assim que a primeira promessa se cumpre.Você também não precisa do .when.Crie uma função que crie um processo de upload como tal:

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

Agora você pode mapear os arquivos para tarefas:

 var tasks = files.map(createUploadTask);

Observe que agora as tarefas são cada funções que retornam uma promessa sobre o upload de um arquivo.Não são promessas.

Agora você pode encadeá-los:

 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;

Agora você também não precisa usar quando, já que retorna um solteiro promessa.Presumo que você não precise do resultado real aqui (mas apenas para saber o sucesso/fracasso).

Você simplesmente faz:

 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; 
 }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top