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...")) }