Основная проблема заключается в том, что код не ожидает выполнения обещаний, возвращаемых s3_update
, и заполняет массив aws_url
, прежде чем разрешить обещание, возвращенное upload_to_aws
, с этим массивом (он все еще пуст).
До сих пор это часто задаваемый вопрос, адресованный в Как я могу вернуть ответ от асинхронного вызова? , но заменяя метод s3.putObject
для вызова AJAX.
Вы также хотите подождать несколько (ноль или более обещаний), поскольку количество запросов определяется данными.Ожидание завершения нескольких обещаний включает использование Promise.all
.
Выполненное значение вызова Promise.all
представляет собой массив выполненных значений обещаний, представленных в качестве аргументов, в порядке аргументов.Другими словами, выполненное значение - это массив aws_url
, используемый в посте.
(непроверенный) подход, который можно попробовать, с небольшими изменениями для объявления всех переменных и упрощения s3_upload
может быть:
function upload_to_aws(data) {
var loan_application_id = $('#loan_application_id').val();
var s3BucketName = data.bucket_name;
var s3RegionName = data.region;
AWS.config.update({accessKeyId: data.key, secretAccessKey: data.secret_key, region: s3RegionName});
var s3 = new AWS.S3({params: {Bucket: s3BucketName, Region: s3RegionName}});
var urlPromises = [];
$('.attached_image').each(function() {
if($(this).attr('src') != "/assets/upload_bg.png" && $(this).attr('src') != '' ) {
var timestamp = (new Date()).getTime();
var randomInteger = Math.floor((Math.random() * 1000000) + 1);
var filename = 'self_evaluation_images/'+ loan_application_id + '_self_eval_ic_' + timestamp + '.png';
var u = $(this).attr('src').split(',')[1];
var binary = atob(u);
var array = [];
for (var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
var typedArray = new Uint8Array(array);
urlPromises.push( s3_upload(s3, filename, typedArray))
}
});
return Promise.all( urlPromises);
}
function s3_upload(s3, filename, typedArray) { // promisify a call back
return new Promise(function(resolve, reject) {
s3.putObject(
{Key: filename, ContentType: 'image/png', Body: typedArray.buffer, ContentEncoding: 'base64', ACL: 'public-read'},
function(err, data) { err ? reject(err) : resolve( data);}
);
});
}
(Если какая-либо из объявленных переменных должна быть глобальной, удалите перед ними var
).
Обещание, возвращаемое при вызове upload_to_aws
, должно быть выполнено с нулевым массивомили более загруженных URL:
$.when(upload_to_aws(data.data)).then(function(aws_uploaded_urls {
console.log(aws_uploaded_urls);
})
Совместимость с JQuery (обновление)
До версии 3 jQuery не выполнял обещания, соответствующие Спецификация обещаний Aplus или более поздняя версия ECMAScript версии 6.Более старые версии JQuery способны вообще не распознавать обещания ES6 как обещания и не могут дождаться их выполнения.
Убедитесь, что вы используете JQuery 3 или более позднюю версию с кодом, который использует собственные обещания.Если вам нужна поддержка браузеров IE, вам также необходимо включить обещания в стиле polyfill для ES6, поддерживающие Promise.all
.
Если вам требуется поддержка браузеров, которые больше не поддерживаются в JQuery 3 рассмотреть возможность полного удаления использования Promise и, скажем, рефакторинга кода вокруг использования объектов Deferred (за рамками этого ответа).Это также устранит необходимость в полизаполнении в старых браузерах, в которых отсутствует встроенная поддержка Promise.
Если метод .when
создает проблемы в связи с использованием обещания ES6, рассмотрите возможность вызова кода на простом JavaScript:
upload_to_aws(data.data)
.then(function(aws_uploaded_urls) {
console.log(aws_uploaded_urls);
})
.catch( function( err) {
console.log( "upload_to_aws failed: ", err);
}