DisplayScript

Asynchronous Operations and Readiness

Image decoding, text layout, filesystem operations and network requests can all take a long time to finish. DisplayScript uses state variables to do these tasks asynchronously, typically with code that looks something like:

state(imageLoader) loader = __unit
loader <- {
    url: name,
    handler: function (imageInfo) {
        // Use the loaded image somewhere.
    }
}

Callback-driven operations like this are awkward to use, though. Imagine if you had to write the following code every time you wanted to draw an image!

state imageState = #init
switch imageState {
case #init:
    loadImage("http://i.imgur.com/RIxqgj9.jpg", function (imageInfo) {
        next imageState = #loaded(imageInfo)
    })
    next imageState = #loading
    draw loadingSpinner
case #loading:
    draw loadingSpinner
case #loaded(info):
    draw imageWithInfo(info)
}

We could abstract this into a drawable function, but we'd still have to specify what is drawn while the image loads. Loading spinners aren't appropriate for many images. For example, we'd like to wait for a button image to finish loading before drawing it.

Our solution is to let the image declare itself "not ready" until it has finished loading. Trying to draw or measure something before it's ready will stop drawing the current frame. The __notReady statement declares that the current drawable is "not ready".

drawable image(url) {
    state imageState = #init
    switch imageState {
    case #init:
        loadImage(url, function (imageInfo) {
            next imageState = #loaded(imageInfo)
        })
        next imageState = #loading
        __notReady
    case #loading:
        __notReady
    case #loaded(info):
        draw imageWithInfo(info)
    }
}

Now we can draw an image without thinking about asynchronousness at all:

draw image("http://i.imgur.com/RIxqgj9.jpg")

This will block the UI when the image is not ready. To show loading text instead, we can call the ready function on the image and draw the text instead of the image if the image isn't ready yet:

var img = image("http://i.imgur.com/RIxqgj9.jpg")
if ready(img) {
    draw img
} else {
    draw centered(text("Loading..."))
}