How can I return a value from two asynchronous method?

I am trying to create an image compressor without any third-party library.

Here is my code(it will finally returns the blob of compressed image):

 async function CompressImage(File, Quality, FileType) {
        var canvas = document.createElement('canvas');
        var ctx = canvas.getContext('2d');
        var img = new Image;
        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 blob;
                }, FileType, Quality);            
            }
        }
        img.src = URL.createObjectURL(File);
    }

It seems there is nothing wrong with my code while always returns an undefined value.

I found out maybe it is the asynchronous problem. The img.onload and the canvas.toBlob are asynchronous methods.

I think the promise may help this while I don’t know how to order these methods run one by one and finally returns a value.

How can I solve this? Thank you.

74 thoughts on “How can I return a value from two asynchronous method?”

  1. 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.

    Reply

Leave a Comment