0

I'm building a webapp that needs to display TIFF images in the browser (among other things) and am hitting a wall trying to properly organize the data.

I want to create a class that will hold the relevant data for the items I am working with so it can be used through class methods. Because the data is retrieved through parsing a TIFF file from a server, this needs to be accomplished through something like the Fetch API.

In the example below I am fetching a TIFF from a local URL using Chrome Web Server, then creating and displaying it in a canvas using Tiff.js (https://github.com/seikichi/tiff.js/tree/master):

class boardImage {
    constructor(boardType) {
        this.boardType = boardType;
        this.boardURL = 'http://127.0.0.1:8887/28431413.Display.' + this.boardType + '.tif'
        this.canvas;
        this.width;
        this.height;
        this.loadImage()
    }

    async loadImage() {
        fetch(this.boardURL)
            .then((response) => {
                response.arrayBuffer().then((buffer) => {
                    let tiff = new Tiff({buffer: buffer});
                    this.width = tiff.width();
                    this.height = tiff.height();
                    this.canvas = tiff.toCanvas();
                    if (this.canvas) {
                        this.canvas.classList.add("boardimage");
                        this.canvas.setAttribute('style', 'width:' + this.width + 'px; height: ' + this.height + 'px;');

                        // this works but I don't want to call this here
                        this.displayImage();
                    }
                })
            })
    }

    displayImage() {
        document.getElementById("boardview").append(this.canvas);
    }

}

The above code works because displayImage() is called within the chain. When I call it outside of the chain the canvas images are undefined. In either situation the class members are set properly and I can see the appropriate values for canvas, width, and height in the browser console. I would like to just load the relevant data when I instantiate the class, and the call methods like displayImage() or reference member variables when they need to be.

I understand that this is asynchronous behavior, but I don't know how to handle it properly. Thanks!

Casey Nord
  • 15
  • 10
  • Have a look at [Is it bad practice to have a constructor function return a Promise?](https://stackoverflow.com/q/24398699/1048572) for a proper solution – Bergi Jun 20 '19 at 14:14
  • 1
    Thank you @Bergi, that link has some good explanations that outline some better looking approaches to this. – Casey Nord Jun 20 '19 at 16:32

1 Answers1

-1

Here is a way to do it with async and await:

class boardImage {
constructor(boardType) {
    this.boardType = boardType;
    this.boardURL = 'http://127.0.0.1:8887/28431413.Display.' + this.boardType + '.tif'
    this.canvas;
    this.width;
    this.height;
}

async loadImage() {
    let response = await fetch(this.boardURL)
    let buffer  = await response.arrayBuffer()
    let tiff = new Tiff({buffer: buffer});
    this.width = tiff.width();
    this.height = tiff.height();
    this.canvas = tiff.toCanvas();
    if (this.canvas) {
        this.canvas.classList.add("boardimage");
        this.canvas.setAttribute('style', 'width:' + this.width + 'px; height: ' + this.height + 'px;');
    }
}

displayImage() {
    document.getElementById("boardview").append(this.canvas);
}

}

let image = new boardImage("example")
image.loadImage().then((response) => {
    image.displayImage()
})

You create a boardImage then call the loadImage method which returns a promise. After the promise resolves you display the canvas.

Hopefully, this helps.

Casey Nord
  • 15
  • 10
anbcodes
  • 849
  • 6
  • 15
  • you could do that ... if loadImage returned a promise, which it doesn't – Jaromanda X Jun 19 '19 at 23:36
  • @JaromandaX loadImage is async which means it returns a promise even if it is not specified. – anbcodes Jun 19 '19 at 23:48
  • sorry, I meant to say that it returns a pointless promise unrelated to the asynchronous task within it – Jaromanda X Jun 19 '19 at 23:53
  • @JaromandaX I edited my post to show a better way of doing it. – anbcodes Jun 20 '19 at 14:04
  • No, that's not a good way of doing it. Don't go back to callbacks without error handling when you already have promises. – Bergi Jun 20 '19 at 14:16
  • @Bergi I am sorry, I think I might have given a better answer now. – anbcodes Jun 20 '19 at 14:46
  • Thanks for the edit. Still not optimal (the class methods can't be used arbitrarily, `displayImage` doesn't need an instance but takes all data via arguments), but much better. – Bergi Jun 20 '19 at 14:54
  • @Bergi Are you saying I should remove the argument and make it use `this.canvas`? I thought making `displayImage` take an argument would prevent it from being called before loading. – anbcodes Jun 20 '19 at 14:58
  • This answer works and gets the job done. I appreciate the feedback from @Bergi, and the link posted in the question above that provides insight to some better ways of handling this – Casey Nord Jun 20 '19 at 16:45