Powerful async data flow is only possible if combining the individual actions into a chain of promises is simple and hides the complexity, rather than increases it. Let me show how combining promises can be simple if we separate building individual steps from linking them into a directed graph.
Let us take a couple of async functions to play with
1 | var q = require('q'); |
Let us add two numbers, multiply that sum and then verify the result. We will place wrong value into the verify function on purpose.
1 | add(2, 3) |
Notice that function multiply
just passes the result argument to the double
function.
We can shorten our code a lot by removing the insignificant variable. The promise
engine will just call double
directly instead of calling multiply
.
1 | add(2, 3) |
This is an example of point-free programming and I consider it a very useful technique to remove syntax noise from my code.
Tap into promise chain using Lodash
We still need to see why our assertion throws an error.
Let us print the value passed between the add
and double
steps. We can use lodash/tap method
to pass a value to an interceptor function, and then return it. The _.tap
method takes value first,
and the interceptor function second. This is why we need to partially apply console.log
from the right
to leave the first argument free.
1 | var _ = require('lodash'); |
We found the problem: the sum is 5, that means its doubled value should be 10. We can fix the error, but
notice that we are mixing two concerns in the above code. We are linking actions into a chain using .then
method. We also have complex logic that creates two steps right in place (lines // 1
and // 2
).
It is hard to tell what each action does unless you look into the code, which might be hard. The code is terse,
and due to its point-free nature there are no function names to give us hints. I recommend
forming actions separately and use good variable names for each step.
1 | var _ = require('lodash'); |
This is much simpler code to understand. Can we do better? Yes, we remove a little more code.
Let us rewrite verify
function via equality and compose functions.
1 | var print = _.partialRight(_.tap, console.log); |
Using Ramda to shorten code
We can shorten the code by switching from lodash to Ramda. There are several good reasons, but in this example, the currying by default + different argument order is the important part.
1 | var R = require('ramda'); |
Recently, Ramda has added methods to compose promise returning functions (pCompose and pPipe).
In our example, they can be used to shorten the actual chaining and remove triple .then
call.
1 | var R = require('ramda'); |
I still build individual actions separately from chaining the promise steps.
Multiple promise tracks
R.pPipe is a great tool, because it allows us to quickly create separate tracks of actions to take. For example, let us start by adding two numbers, and then multiply by 2 and verify the result. At the same time let us multiply the sum by 3. When both forks finish, let us print the 2 results.
add(2, 3)
\___ multiply by 2 -> verify equals 10 --\
\___ multiply by 3 ----------------------\
\--> print [value1, value2]
We can use Ramda's built-in addition and multiplication functions to replace our add
and mul
functions
1 | var R = require('ramda'); |
Notice that we use R.tap(verify)
in order to return the input value after verifying.
We can probably simplify splitMultiply
further, but even now it is very short and clear.
Update 1
Promise library Q has introduced new method API to tap into the promise chain Q.tap. You can now directly tap into a chain without writing a method yourself
1 | Q('something') |