Como usar o quando então enviar upload de arquivos sequencialmente em uma função que também é uma promessa diferida?
-
26-12-2019 - |
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.
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;
}