Я хочу, чтобы загрузить файл на сервер, а затем получить URL-адрес загруженного файла и просмотреть его. Файлы могут быть более одного. Для этого я написал следующий код:
let filesURL=[];
let promises=[];
if(this.state.files_to_upload.length>0) {
for(let i=0; i<this.state.files_to_upload.length; i++) {
promises.push(this.uploadFilesOnServer(this.state.files_to_upload[i]))
}
Promise.all(promises).then(function(result){
console.log(result);
result.map((file)=>{
filesURL.push(file);
});
});
console.log(filesURL);
}
const uploadedFilesURL=filesURL;
console.log(uploadedFilesURL);
console.log(filesURL);
дайте мне значения, возвращенные Promise.all
.
И я хочу использовать эти значения только тогда, когда Promise.all
завершится правильно. Но я столкнулся с проблемой, что строки console.log(uploadedFilesURL);
excuz первый независимо от Promise.all
и дать мне undefined
values.I думаю, что я не использую обещания правильно, может кто-нибудь, пожалуйста, помогите мне?
Код uploadFileOnServer
:
uploadFilesOnServer(file)
{
let files=[];
let file_id='';
const image=file;
getImageUrl().then((response) => {
const data = new FormData();
data.append('file-0', image);
const {upload_url} = JSON.parse(response);
console.log(upload_url);
updateProfileImage(upload_url, data).then ((response2) => {
const data2 = JSON.parse(response2);
file_id=data2;
console.log(file_id);
files.push(file_id);
console.log(files);
});
});
return files;
}
Нет, обещание асинхронно и, как таковое, не работает так, как вы думаете. Если вы хотите выполнить что-то после завершения обещания, вы должны поместить его в обещание, а then
обратный вызов. Вот пример, основанный на вашем коде:
uploadFilesOnServer(file) {
let files=[];
let file_id='';
const promise = getImageUrl()
.then((imageUrlResponse) => {
const data = new FormData();
data.append('file-0', file);
const { upload_url } = JSON.parse(imageUrlResponse);
console.log(upload_url);
return updateProfileImage(upload_url, data);
})
.then ((updateImageResponse) => {
file_id= JSON.parse(updateImageResponse);
console.log(file_id);
files.push(file_id);
console.log(files);
return files;
});
return promise;
}
let filesPromise = Promise.resolve([]);
if(this.state.files_to_upload.length > 0) {
const promises = this.state.files_to_upload.map((file) => {
return this.uploadFilesOnServer(file);
});
filesPromise = Promise.all(promises).then((results) => {
console.log(results);
return [].concat(...results);
});
}
// This is the final console.log of you (console.log(uploadedFilesURL);)
filesPromise.then((filesUrl) => console.log(filesUrl));
Хорошая книга, посвященная ES6 вообще и обещаниям, в частности, эта книга. Понимание ECMAScript 6 - Николас К. Закас
Редактировать:
Вот простое объяснение кода примера:
UploadFilesOnServer - это функция, которая берет файл, загружает его и возвращает URL-адрес файла, когда загрузка завершается в будущем в виде обещания. Обещание будет вызывать его then
обратный вызов, когда он получает URL.
Используя функцию map, мы создаем список обещаний url, результаты, которые мы получили от выполнения uploadFilesOnServer
для каждого файла в списке.
Метод Promise.all
ожидает выполнения всех обещаний в списке, присоединяется к списку результатов URL и создает обещание с результатом, который является списком адресов. Нам это нужно, потому что нет гарантии, что все обещания будут завершены сразу, и нам нужно собрать все результаты в один обратный вызов для удобства.
Мы получаем ссылки из then
обратного вызова.
Вы должны сделать это на .then
части вашего Promise.all()
Promise.all(promises)
.then(function(result){
console.log(result);
result.map((file)=>{
filesURL.push(file);
});
return true; // return from here to go to the next promise down
})
.then(() => {
console.log(filesURL);
const uploadedFilesURL=filesURL;
console.log(uploadedFilesURL);
})
Так работает асинхронный код. Вы не можете ожидать свой console.log(filesURL);
правильно работать, если он называется синхронно после асинхронного вызова для извлечения файлов с сервера.
Что касается вашего кода, то есть несколько проблем:
1. uploadFilesOnServer
должен возвращать Promise, поскольку он isync. Следовательно:
uploadFilesOnServer(file)
{
let files=[];
let file_id='';
const image=file;
return getImageUrl().then((response) => {
const data = new FormData();
data.append('file-0', image);
const {upload_url} = JSON.parse(response);
console.log(upload_url);
updateProfileImage(upload_url, data).then ((response2) => {
const data2 = JSON.parse(response2);
file_id=data2;
console.log(file_id);
files.push(file_id);
console.log(files);
return files;
});
});
}
2. Внутри основного тела функции вы можете оценивать результаты выполнения Promise.all
только в соответствующем handler
.
В качестве дополнительной заметки я бы посоветовал вам использовать функции es7 async/await с некоторыми транспилерами, такими как babel/typescript. Это значительно сократит вложенность/осложнения при написании такого кода.
console.log(uploadedFilesURL);
будет выполнен немедленно. Код, который необходимо выполнить после разрешения обещания,.then()
обратный вызов.then()
. Во-вторых,Promise.all()
принимает в качестве аргумента массив обещаний, а вы передаете ему массив любого типаfile_id
.