]> git.r.bdr.sh - rbdr/r.bdr.sh/blame - jekyll/_posts/2014-07-04-fluorine-the-library-to-get-things-flowing.md
Self hosts image on fluorine article
[rbdr/r.bdr.sh] / jekyll / _posts / 2014-07-04-fluorine-the-library-to-get-things-flowing.md
CommitLineData
7f50b5b5
BB
1---
2layout: post
3title: "Fluorine: the library to get things flowing"
4tags: english javascript
5color: cyan
6header_image: fluorine.gif
7description: "Fluorine is a flow based library that makes complex
8async operations really simple."
9---
10
11Today I was happily surprised with the fact that [fluorine ][fluorine]
12has been made publicly available with an MIT License!
13This is a library created by [azendal][azendal]
14as a way to solve some of the more complex flows in [breezi][breezi].
15The problem had been bugging us for a while and the several existing
16solutions to the async problem didn't feel right for it, and then
17fluorine arrived to the [neon][neon] [family][cobalt] [of][thulium]
18[libraries][tellurium].
19
20Fluorine is a really simple flow abstraction for javascript that doesn't
21do much: You can have a *Flow*, which has a series *Steps*; a step has a
22name, a list of dependencies, and a fulfill function. The way it works
23is that flows run automatically, and every step that can be taken *is
24taken*, then its dependents are executed, and so on until all nodes are
25fulfilled. That's all there is to it, and that's all it needs to provide
26powerful asynchronous constructs that can stay decoupled.
27
28But let's code a simple example:
29
30In the following example we're loading an application where we allow our
31user to view a 3D model of their favorite 1985 vintage action figures in
32a fake shelf. We *show a loading screen* while we *get the data for the
33models*, then *fetch the models themselves*, also *request the user data*
34(so we can see their favorites), plus *all the latest posts* from the most
35popular social network for enthusiasts of 1985 vintage action figures
36— G.I. Yo. Once we have the model and user data, we can *display the shelf*
37so the user can see the name of 1985 snowman and finally settle that argument
38with joe about which 1985 vintage action figure is the best 1985 vintage
39action figure, so we have to: *hide the loading scren*, show the models
40with a generic image and *leave a loader in the social network box*.
41Once the models are fetched, we can start *displaying them* in the shelf
42instead of the generic image while it loaded; once we have the social
43networks, we *put the posts in there*. So, that's easy right?, no chance
44of turning *this* into a mess...
45
46![A boring old diagram][fig1]
47*34 indentations later...*
48
49I put this in a diagram with handy color coded arrows so we don't get
50lost. Now we know what operations depend on each other, and that's all we
51need for fluorine. So, we can now turn that diagram into a flow:
52
53<pre class="language-javascript"><code class="language-javascript">
54 var giFlow = new Flow();
55
56 // Top Row Nodes (as shown in the diagram)
57
58 giFlow.step('showLoadingScreen')(function (step) {
59 loadingScreen.show();
60 step.success();
61 });
62
63 giFlow.step('fetchUserData')(function (step) {
64 UserData.fetch(function (userData) {
65 step.success(userData);
66 });
67 });
68
69 giFlow.step('fetchModelData')(function (step) {
70 ModelData.fetch(function (modelData) {
71 step.success(modelData);
72 });
73 });
74
75 giFlow.step('fetchSocialData')(function (step) {
76 SocialData.fetch(function (socialData) {
77 step.success(socialData);
78 });
79 });
80
81 // Middle Row Nodes (as shown in the diagram)
82
83 giFlow.step('hideLoadingScreen').dependsOn('showLoadingScreen', 'fetchUserData', 'fetchModelData')(function (step) {
84 loadingScreen.hide();
85 step.success();
86 });
87
88 giFlow.step('displayPlaceholderSocialBox').dependsOn('fetchUserData', 'fetchModelData')(function (step) {
89 socialBox.display(function () {
90 step.success();
91 });
92 });
93
94 giFlow.step('displayModelShelf').dependsOn('fetchUserData', 'fetchModelData')(function (step) {
95 modelShelf.display(step.data.fetchUserData, step.data.fetchModelData, function () {
96 step.success();
97 });
98 });
99
100 giFlow.step('fetchModels').dependsOn('fetchModelData')(function (step) {
101 Model.fetch(step.data.fetchModelData, function () {
102 step.success();
103 });
104 });
105
106 // Bottom Row Nodes (as shown in the diagram)
107
108 giFlow.step('fillInGIYoPosts').dependsOn('fetchSocialData', 'displayPlaceholderSocialBox')(function (step) {
109 socialBox.fillInGIYOPosts(step.data.fetchSocialData, function () {
110 step.success();
111 });
112 });
113
114 giFlow.step('replaceModels').dependsOn('fetchModels', 'displayModelShelf')(function (step) {
115 modelShelf.replaceModels(step.data.fetchModels, function () {
116 step.success();
117 });
118 });
119</code></pre>
120
121That's all there is to fluorine: you define steps, you fulfill them.
122Every step may have dependents, and it can access their data from the
123callback! Now you can define complex operations step by step, you could
124create complex branching by not triggering a step's success method.
125Another important part of fluorine is that modifying flows is really
126easy, as long as you have clearly defined nodes, you can just move
127around dependencies to alter the way the program runs!
128You can even check your work against the spec by converting it
129to dot format (eg. `giFlow.toDot()`):
130
131 digraph {
132 showLoadingScreen
133 fetchUserData
134 fetchModelData
135 fetchSocialData
136 hideLoadingScreen
137 showLoadingScreen -> hideLoadingScreen
138 fetchUserData -> hideLoadingScreen
139 fetchModelData -> hideLoadingScreen
140 displayPlaceholderSocialBox
141 fetchUserData -> displayPlaceholderSocialBox
142 fetchModelData -> displayPlaceholderSocialBox
143 displayModelShelf
144 fetchUserData -> displayModelShelf
145 fetchModelData -> displayModelShelf
146 fetchModels
147 fetchModelData -> fetchModels
148 fillInGIYoPosts
149 fetchSocialData -> fillInGIYoPosts
150 displayPlaceholderSocialBox -> fillInGIYoPosts
151 replaceModels
152 fetchModels -> replaceModels
153 displayModelShelf -> replaceModels
154 }
155
156 dot -Tpng that_file_up_there.dot -o fancy_graphic.png
157
158![Fancy Graphic][fig2]
159
160They look the same (ish)! It even did a better job at putting it together!
161
162So that's fluorine, a pretty simple flow tool that can let you build
163complex stuff, you could implement a neural network, or a behavior tree
164for a game actor, or a loop of events, or do some complex distributed
165tasks, you can use it for almost anything as long as you can graph it,
166because that's all it really is: graphs. You could even create dynamic
167nodes and have a flow that is created programatically!
168
169I hope that example was enough to get you interested on how you can use
170it now that it's open to everyone! Have fun :D
171
172[fluorine]: https://github.com/freshout-dev/fluorine
173[azendal]: https://github.com/azendal
174[breezi]: https://breezi.com
175[neon]: https://azendal.github.io/neon
908c36a7 176[cobalt]: https://github.com/rbdr/cobalt
7f50b5b5
BB
177[thulium]: https://freshout-dev.github.io/thulium
178[tellurium]: https://github.com/azendal/tellurium
179[fig1]: https://docs.google.com/drawings/d/1a3xVTO0qfSqt_fKWuKzeQFGbtOvQbfVV63_nGD5xcq0/pub?w=960&h=720
df0b395b 180[fig2]: /img/fluorine.png