我有以下功能,我更愿意同步处理,但不知怎的,它无法正常工作。
function upload_to_aws(data) {
return new Promise(function(resolve, reject) {
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}});
aws_url= []
$('.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);
filename = 'self_evaluation_images/'+ loan_application_id + '_self_eval_ic_' + timestamp + '.png';
var u = $(this).attr('src').split(',')[1],
binary = atob(u),
array = [];
for (var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
var typedArray = new Uint8Array(array);
s3_upload(s3, filename, typedArray).then(function(url_aws) {
aws_url.push(url_aws);
console.log(aws_url)
console.log(aws_url.length)
})
}
})
resolve(aws_url);
})
}
function s3_upload(s3, filename, typedArray) {
return new Promise(function(resolve, reject) {
s3.putObject({Key: filename, ContentType: 'image/png', Body: typedArray.buffer, ContentEncoding: 'base64', ACL: 'public-read'},
function(err, data) {
if (data !== null) {
url_aws = s3.endpoint.href + filename;
resolve(url_aws)
}
else {
reject(err);
}
});
})
}
当调用此函数时,它调用upload_to_aws
函数,我希望在该函数返回给我的aws_uploaded url数组之前执行所有操作。
$.when(upload_to_aws(data.data)).then(function(aws_uploaded_url) {
console.log(aws_uploaded_url);
})
但目前基本上发生的事情是,在将图像上传到s3的过程中,即使在将图像上传到s3之前,这也会被称为resolve(aws_url)
,因此将console.log(aws_uploaded_url)
打印为空数组[]
因为函数没有完全执行。
还有其他方法可以在javascript中处理回调和同步函数吗?
答案 0 :(得分:1)
主要问题是代码不会等待s3_update
返回的promise被解决并填充aws_url
数组,然后再解析upload_to_aws
与该数组返回的promise(它还是空的。)
到目前为止,这是How do I return the response from an asynchronous call?中常见问题,但用s3.putObject
方法代替AJAX调用。
您还希望等待多个(零个或多个承诺),因为请求数由数据决定。等待多个承诺完成涉及使用Promise.all
。
Promise.all
调用的实现值是作为参数提供的promise的已满足值的数组,按提供的参数顺序排列。换句话说,实现的值是帖子中使用的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
返回的承诺应使用零个或多个上传网址的数组来实现:
$.when(upload_to_aws(data.data)).then(function(aws_uploaded_urls {
console.log(aws_uploaded_urls);
})
<小时/> JQuery兼容性(更新)
在版本3之前,jQuery没有实现符合Aplus promise specificaion或后来的ECMAScript版本6标准的promise。较旧版本的JQuery无法将ES6承诺视为承诺而无法等待它们得到解决。
检查您使用的是使用JQuery 3或更高版本的代码,该代码使用本机Promise。如果您需要支持IE浏览器,您还需要包含支持Promise.all
的ES6样式承诺的polyfill。
如果您需要支持不再supported in JQuery 3的浏览器,请考虑完全删除Promise使用,例如,围绕使用Deferred对象重构代码(在本答案的范围之外)。这也将消除在缺乏原生Promise支持的旧浏览器中对polyfill的需求。
如果.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);
}
答案 1 :(得分:0)
因为每次迭代都在运行异步,所以需要使用Promise.all
,将每次迭代映射到Promise
,最后解析upload_to_aws
一旦所有迭代的承诺返回的承诺完成:
function upload_to_aws(data) {
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}});
return Promise.all(
[...document.querySelectorAll('.attached_image')]
.map(image => new Promise((resolve, reject) => {
const { src } = image;
if(src != "/assets/upload_bg.png" && src != '' ) {
var timestamp = (new Date()).getTime();
var randomInteger = Math.floor((Math.random() * 1000000) + 1);
filename = 'self_evaluation_images/'+ loan_application_id + '_self_eval_ic_' + timestamp + '.png';
var u = src.split(',')[1],
binary = atob(u),
array = [];
for (var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
var typedArray = new Uint8Array(array);
s3_upload(s3, filename, typedArray).then(resolve, reject);
}
}))
);
}