Capture result of HTML5 FileReader when using promises in async

1.7k views Asked by At

I have an Angular 4 application where I am reading an image & trying to pass the base64 string to another variable - however I am having an issue due to the async nature of this - the image.src is empty and therefore the value has passed correctly to the image object?

ngAfterViewInit(): void {
    let image = new Image();
    var promise = this.getBase64(fileObject, this.processImage());
    promise.then(function(base64) {
        console.log(base64);    // outputs string e.g 'data:image/jpeg;base64,........'
    });

    image.src = base64; // how to get base64 string into image.src var here??

    let editor = new PhotoEditorSDK.UI.ReactUI({
        container: this.editor.nativeElement
        editor: {
           image: image
        }
    });
}

/**
 * get base64 string from supplied file object
 */
public getBase64(file, onLoadCallback) {
    return new Promise(function(resolve, reject) {
        var reader = new FileReader();
        reader.onload = function() { resolve(reader.result); };
        reader.onerror = reject;
        reader.readAsDataURL(file);
    });
}

public processImage() {

}
3

There are 3 answers

0
Dipen Shah On BEST ANSWER

As the code is asynchronous in nature, you will have to wait for the result if you want to use it. In your case, you will have to wait until your promise is resolved. So your code should look like:

ngAfterViewInit(): void {
   let image = new Image();
   var promise = this.getBase64(this.fileObject, this.processImage);
   promise.then(function(base64: string) {
       console.log(base64);    
       image.src = base64;

       let editor = new PhotoEditorSDK.UI.ReactUI({
           container: this.editor.nativeElement
           editor: {
             image: image
           }
       });
   });
}

I have changed following things:

  1. I moved code for ReactUI instantiation inside promise callback
  2. Specified type of base64 parameter as string
  3. Fixed typo in getBase64 function call, from this.processImage() to this.processImage so it is passing callback function and not result of processImage function.

I hope this helps!

2
Bunyamin Coskuner On

promise.then is an async function which means the code comes after this statement will be executed first and then callback function within then will be executed. So, you should move your code into callback of then.

promise.then((base64: string) => {
    console.log(base64);    // outputs string e.g 'data:image/jpeg;base64,........'
    console.log(typeof base64); // type is string
    image.src = base64;

    let editor = new PhotoEditorSDK.UI.ReactUI({
        container: this.editor.nativeElement
        editor: {
           image: image
        }
    });
});

Edit: I changed the callback function to fat arrow function () => {}, it's because you are accessing one of the component fields by this.editor, and functions have their own scope. You may get an error saying cannot read nativeElement of undefined.

0
Titian Cernicova-Dragomir On

Since getBase64 is an async operation you can't use it's result right away. You need to wait for it either by calling then (as you have already experimented with) or using async/await.

The async/await version (which is also part of the es 2017 standard) is simpler to understand especially if you are new to async programming. A general rule of thumb, if you see a Promise and need to use the value you will need to use the await operator (and make your method async)

async ngAfterViewInit() {
    let image = new Image();
    var base64 = await this.getBase64(fileObject, this.processImage());

    image.src = base64; //Now available

    let editor = new PhotoEditorSDK.UI.ReactUI({
        container: this.editor.nativeElement
        editor: {
           image: image
        }
    });
}
public getBase64(file, onLoadCallback) {
    return new Promise<string>(function(resolve, reject) {
        var reader = new FileReader();
        reader.onload = function() { resolve(reader.result as string); };
        reader.onerror = reject;
        reader.readAsDataURL(file);
    });
}

You can also move your code to the callback passed to a then call, but this may be a bit harder to read (especially if you are just starting out with async code) and if you have to orchestrate multiple async operations reading the code quickly becomes an issue

async ngAfterViewInit() {
    let image = new Image();
    this.getBase64(fileObject, this.processImage())
        .then(base64=>
        {
            image.src = base64; // available now 

            let editor = new PhotoEditorSDK.UI.ReactUI({
                container: this.editor.nativeElement
                editor: { image }
            });
        });
}