Use a Promise
instead of async
, in the end is the same but syntactically are not, in a Promise you get 2 functions as parameters (typically called resolve
and reject
), these functions can be run inside any callback no problem, but in an async
function becomes problematic because there's no way to await
for a function that receives a callback (because is not a Promise that you can await
).
TLDR;
async
functions do not play well executing functions that run callbacks, use a Promise directly so you can run resolve()
anywhere, even if is on a callback function.
For example:
function CompressImage(File, Quality, FileType) {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var img = new Image;
var prom = new Promise(function(resolve) {
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
canvas.toBlob(function (blob) {
return resolve(blob);
}, FileType, Quality);
}
}
img.src = URL.createObjectURL(File);
});
return prom;
}
That should do the trick, look how I'm creating the promise to resolve it with the callback of canvas.toBlob
, if you notice, the function is not async
because is returning a Promise directly, but in your code, you can treat it just like an async function.
Another way to do it with a little more updated syntax can be:
const CompressImage = async (File, Quality, FileType) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image;
return await (new Promise(resolve => {
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
canvas.toBlob(resolve, FileType, Quality);
}
}
img.src = URL.createObjectURL(File);
}));
};
And should be the same.
I haven't tested this but if is broken is pretty close.
UPDATE:
Well, after playing with the jsfiddle you provided I have an interesting finding:
$(function () {
$("#FileUploader").change(function () {
StartCompress(this.files[0]);
});
function StartCompress(input) {
$("#beforesize").text(input.size);
CompressFile(input, 0.8).then(blob => $("#afteresize").text(blob.size));
}
function CompressFile(file, quality) {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
var prom = new Promise(resolve => {
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
canvas.toBlob(blob => {
resolve(blob)
}, file.type, quality);
}
img.src = URL.createObjectURL(file);
});
return prom;
}
});
I modified a little bit the code so my ocd can be in peace hahaha, but also, the problem is the MIME (FileType
), because you are not providing canvas
with the file type, is just grabbing it and converting it to png
and that's a lossless format, so it will be bigger if you are selecting a jpg image (that allows loss) and basically scaling it to fit the same size.
Try the code now (I'm using file.type
to provide the type of the file to canvas.toBlob
), It goes to hell with png
and I suppose is related to the issue I pointed out.
Hope it helps.