]> git.r.bdr.sh - rbdr/r.bdr.sh/blob - 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
1 ---
2 layout: post
3 title: "Fluorine: the library to get things flowing"
4 tags: english javascript
5 color: cyan
6 header_image: fluorine.gif
7 description: "Fluorine is a flow based library that makes complex
8 async operations really simple."
9 ---
10
11 Today I was happily surprised with the fact that [fluorine ][fluorine]
12 has been made publicly available with an MIT License!
13 This is a library created by [azendal][azendal]
14 as a way to solve some of the more complex flows in [breezi][breezi].
15 The problem had been bugging us for a while and the several existing
16 solutions to the async problem didn't feel right for it, and then
17 fluorine arrived to the [neon][neon] [family][cobalt] [of][thulium]
18 [libraries][tellurium].
19
20 Fluorine is a really simple flow abstraction for javascript that doesn't
21 do much: You can have a *Flow*, which has a series *Steps*; a step has a
22 name, a list of dependencies, and a fulfill function. The way it works
23 is that flows run automatically, and every step that can be taken *is
24 taken*, then its dependents are executed, and so on until all nodes are
25 fulfilled. That's all there is to it, and that's all it needs to provide
26 powerful asynchronous constructs that can stay decoupled.
27
28 But let's code a simple example:
29
30 In the following example we're loading an application where we allow our
31 user to view a 3D model of their favorite 1985 vintage action figures in
32 a fake shelf. We *show a loading screen* while we *get the data for the
33 models*, 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
35 popular 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*
37 so the user can see the name of 1985 snowman and finally settle that argument
38 with joe about which 1985 vintage action figure is the best 1985 vintage
39 action figure, so we have to: *hide the loading scren*, show the models
40 with a generic image and *leave a loader in the social network box*.
41 Once the models are fetched, we can start *displaying them* in the shelf
42 instead of the generic image while it loaded; once we have the social
43 networks, we *put the posts in there*. So, that's easy right?, no chance
44 of turning *this* into a mess...
45
46 ![A boring old diagram][fig1]
47 *34 indentations later...*
48
49 I put this in a diagram with handy color coded arrows so we don't get
50 lost. Now we know what operations depend on each other, and that's all we
51 need 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
121 That's all there is to fluorine: you define steps, you fulfill them.
122 Every step may have dependents, and it can access their data from the
123 callback! Now you can define complex operations step by step, you could
124 create complex branching by not triggering a step's success method.
125 Another important part of fluorine is that modifying flows is really
126 easy, as long as you have clearly defined nodes, you can just move
127 around dependencies to alter the way the program runs!
128 You can even check your work against the spec by converting it
129 to 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
160 They look the same (ish)! It even did a better job at putting it together!
161
162 So that's fluorine, a pretty simple flow tool that can let you build
163 complex stuff, you could implement a neural network, or a behavior tree
164 for a game actor, or a loop of events, or do some complex distributed
165 tasks, you can use it for almost anything as long as you can graph it,
166 because that's all it really is: graphs. You could even create dynamic
167 nodes and have a flow that is created programatically!
168
169 I hope that example was enough to get you interested on how you can use
170 it 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
176 [cobalt]: https://github.com/rbdr/cobalt
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
180 [fig2]: /img/fluorine.png