Dataflow async
Library ID dataflow-async
Latest version 1.0.5

Introduction

As already mentioned an Attribute is like a field, but the data has an associated state. Another difference is that all transformations are made asynchronously if not otherwise specified.

For this reasons attributes are the obvious choice when we need to handle some data that could be not immediately available (or not available at all) and in general if we have data that may take a while to calculate. An example could be having on an attribute some remote data that needs to be downloaded.

Last but not least, it doesn't exist a mutable attribute, like in fields, so we cannot directly set a new value inside them.

Under attributes' hood

link

In order to represent this pair of data/state we use the class AttributeData<T>. Internally usually an attribute simply contains a Field<AttributeData<T>>.

For this reason we can call in all attributes the method asField() that will return a Field<AttributeData<T>>, like so:

import com.femastudios.dataflow.async.util.* fun main() { val field /* : Field<AttributeData<Int>> */ = attributeOf(456).asField() println(field.value) }
Field<AttributeData<Integer>> field = Attribute.of(456).asField();

For more information see AttributeData.

Creating attributes

link

To create a field we must call the attributeOf() function, which has multiple versions that accept different inputs:

  • Value: the most simple one simply accepts a value, in which case the returned attribute will have the loaded state.
  • AttributeData: this version accepts an AttributeData instance that reflects the internal state we want to give it.
  • Task: this version accepts a lambda that calculates the value. For more info see Tasks. This version will also return a subclass of attribute: RecomputableAttribute. This class adds the possibility to call the method recompute() to retrigger the task. It also supports two optional parameters:
    • lazy: a boolean value that tells if the attribute should start calculating right away (false) or if it should start when it is requested for the first time (true).
    • flowStrategy: a strategy that defines the behavior of the loading task. See the flow strategy section for more info.

Here's an example lazy = false:

import com.femastudios.dataflow.async.util.* fun main() { //When using lazy=false the attribute starts computing the value right away on a background thread val a = attributeOf(lazy = false) { println("Computing...") 10 } Thread.sleep(100) println(a.value) }
//When using lazy=false the attribute starts computing the value right away on a background thread Attribute<Integer> a = Attribute.of(false, workContext -> { System.out.println("Computing..."); return 10; }); Thread.sleep(10); System.out.println(a.getValue());

And here with lazy = true:

import com.femastudios.dataflow.async.util.* fun main() { //When using lazy=true the attribute is in LOADING until is queried for the first time val a = attributeOf(lazy = true) { println("Computing...") 10 } Thread.sleep(1000); //Wait a bit println("Waited 1s") println("attribute is: " + a.value) //Attribute is still LOADING //Calling `a.value` triggered the computation. Attaching a listener with `listen()` does the same. //Computation starts and the attribute goes to LOADED when computation is finished Thread.sleep(10); //Wait for computation println("attribute is: " + a.value) //Attribute is now LOADED }
//When using lazy=true the attribute is in LOADING until is queried for the first time Attribute<Integer> a = Attribute.of(true, workContext -> { System.out.println("Computing..."); return 10; }); Thread.sleep(1000); //Wait a bit System.out.println("attribute is: " + a.getValue()); //Attribute is still LOADING //Calling `a.getValue()` triggered the computation. Attaching a listener with `listen()` does the same. //Computation starts and the attribute goes to LOADED when computation is finished Thread.sleep(10); //Wait for computation System.out.println("attribute is: " + a.getValue()); //Attribute is now LOADED

From a field

link

If we have a simple field that we want to transform into an Attribute we can call the async() function, like so:

import com.femastudios.dataflow.util.* import com.femastudios.dataflow.async.util.* fun main() { val field = mutableFieldOf(10) println(field.value) //attr will have the LOADED status with the same value as the field val attr = field.async() println(attr.value) //When the field changes, the attribute also changes field.value = 44 println(attr.value) }
Field<Integer> field = Field.of(10); System.out.println(field.getValue()); //attr will have the LOADED status with the same value as the field Attribute<Integer> attr = AttributeUtils.async(field); System.out.println(attr.getValue()); //When the field changes, the attribute also changes field.setValue(44); System.out.println(attr.getValue());

Whenever the backing Field changes the attribute will also change.