¿Cómo utilizar cuándo enviar la carga de archivos de forma secuencial en una función que también es una promesa diferida?

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

Pregunta

Tengo la intención de cargar una serie de archivos usando jQuery.

Esta intención está envuelta en una función llamada uploadFilesUsingAjax();

var uploadFilesPromise = uploadFilesUsingAjax();

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

Necesito esperar a que todos los archivos se carguen correctamente antes de hacer otra cosa.

Adentro uploadFilesUsingAjax(),

Escribí mi código de esta manera

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

Desafortunadamente, no pude cargar muchos archivos antes de ser redirigido a la página de éxito.

Probé varias soluciones de StackOverflow sobre cómo hacer esto.Hasta ahora nada funciona.Por favor avise.

Parte del código se ha truncado para ahorrar espacio.

¿Fue útil?

Solución

Todas tus promesas son paralelas y no secuenciales.

Una promesa representa una ya corriendo tarea.Las promesas en JavaScript, a diferencia de las tareas de C# u otras abstracciones, ya están iniciadas.La forma de representar una tarea que no ha comenzado es una función que devuelve una promesa.

Desde promises[i] ya es una promesa - cuando lo hagas promise.then(object) no agrega un controlador .then sino que regresa inmediatamente. .then ignora cualquier argumento que no sea una función.

Por eso regresa temprano, regresa tan pronto como se cumple la primera promesa.Tampoco necesitas el .when.Cree una función que cree un proceso de carga 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
   }
}

Ahora puedes asignar los archivos a tareas:

 var tasks = files.map(createUploadTask);

Tenga en cuenta que ahora las tareas son cada una funciones que devuelven una promesa sobre la carga de un archivo.No son promesas.

Ahora puedes encadenarlos:

 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;

Ahora tampoco necesitas usar cuándo, ya que devuelves un soltero promesa.Supongo que no necesita el resultado real aquí (sino sólo para conocer el éxito/fracaso).

Simplemente haces:

 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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top