6. RACE STANDINGS
Read the 6 hour solo records
Read the 12 hour solo records
Read the 6 hour team records
Read the 12 hour team records
Merge the results
21. IN ACTION
function divide(numerator, denominator){
return new Promise((resolve, reject)=>{
if (denominator !== 0)
resolve(numerator / denominator)
reject('divide by zero')
})
}
divide(15, 3)
.then(console.log)
.catch(console.error)
22. IN ACTION
function divide(numerator, denominator){
return new Promise((resolve, reject)=>{
if (denominator !== 0)
resolve(numerator / denominator)
reject('divide by zero')
})
}
divide(15, 3)
.then(console.log) // 5
.catch(console.error)
23. IN ACTION
function divide(numerator, denominator){
return new Promise((resolve, reject)=>{
if (denominator !== 0)
resolve(numerator / denominator)
reject('divide by zero')
})
}
divide(10, 0)
.then(console.log)
.catch(console.error)
24. IN ACTION
function divide(numerator, denominator){
return new Promise((resolve, reject)=>{
if (denominator !== 0)
resolve(numerator / denominator)
reject('divide by zero')
})
}
divide(10, 0)
.then(console.log)
.catch(console.error) // ‘divide by zero’
25. ALREADY KNOWN
function divide(numerator, denominator){
return denominator ?
Promise.resolve(numerator / denominator) :
Promise.reject ('divide by zero')
}
divide(15, 3)
.then(console.log) // 5
.catch(console.error)
divide(10, 0)
.then(console.log)
.catch(console.error) // ‘divide by zero’
28. ALL
var tasks = [task1, task2, task3, …]
Promise.all(tasks)
.then(results => {
//… all resolved: process results
}
.catch(err => {
// one rejected: handle err
}
29. RACE STANDINGS
Read the 6 hour solo records
Read the 12 hour solo records
Read the 6 hour team records
Read the 12 hour team records
Merge the results
40. PARTIAL
var _ = require(‘lodash’)
//could also use ramda
function add(x, y){
return x + y
}
var add5 = _.partial(add, 5)
console.log(add5(1)) // 6
41. PARTIAL RIGHT
var _ = require(‘lodash’)
function add(x, y){
console.log(‘x’, x) // x 1
console.log(‘y’, y) // y 5
return x + y
}
var add5 = _.partialRight(add, 5)
42.
43. PIPELINE
$ls -l | grep "Aug" | sort +4n | more
-rw-rw-rw- 1 john doc 11008 Aug 6 14:10 ch02
-rw-rw-rw- 1 john doc 8515 Aug 6 15:30 ch07
-rw-rw-r-- 1 john doc 2488 Aug 15 10:51 intro
-rw-rw-r-- 1 carol doc 1605 Aug 23 07:35 macros
$
44. var _ = require(‘lodash')
function readPeopleFromFile(){
Promise.resolve([
{name: 'Risto', age: 6 },
{name: 'Niilo', age: 8 },
{name: 'Edla', age: 12 }])
}
}
readPeopleFromFile()
//filter the people younger than 10,
//_.filter(collection, [predicate=_.identity], [thisArg])
//order by name
//_.sortBy(collection, [iteratee=_.identity], [thisArg])
PARTIAL IN ACTION
56. SUMMARY
What is a Promise
Consume a promise
Produce a promise
Manage multiple blocking tasks with promises
Chain small tasks
Partial application
Closure
How many of you have written a node program?
How many of you write JavaScript for the front end?
For those of you who are daily users of JavaScript, we are going to start slow and cover what might be some boring details, but as we close in on the end of the session, we will be looking at some functional programming techniques.
My hope no matter your experience level, you will want to go home and play with some of these ideas.
What is a key tenant of Node which makes it different from many programming models?
Non blocking or in maybe a more common parlance asynchronous.
In Node, many operations will not complete right away. Instead of waiting for them to complete, you supply a callback and the system hooks you in when the results are ready.
Here is a straight forward non-blocking call withe a callback. This example is from the fs docs. You ask for the contents of the password file to be read. When the data has been read, or an error occurs, the function you supplied as the second parameter is called. In a simple example it looks straight forward. But lets take a case where a few more things need to be done.
Here is a set of steps for my very first node app a couple years ago. I was helping a local mountain bike race track riders.
I needed to read the results for each race category and then stitch the data together. Notice that it is rather plain and straight forward
I know you can’t read this code. The first thing I want you to see is that the code is not like the requirements. The requirements were a simple list of tasks.
What we have here is a deeply nested set of operations and callbacks. In the first callback you initiate the second step, in the second you initiate the third… etc
Heaven forbid you did like I did the first time and don’t nest the steps. In that case you get no data when you merge the results.
I won’t pretend that I’m the first to suggest that this is a problem.
Lets spend a little time identifying the important details
Is it impossible to make out what this code does? No.
Is there a lot of boiler plate? Oh yeah
I won’t pretend that I’m the first to suggest that this is a problem. Far from it. There are multiple ways to resolve callback hell. Today I want to show you how to do it with promises. But before we get to showing that solution, lets delve into what a promise is.
A promise isn’t a new idea. Promise libraries have existed for a few years. This is a smattering of the available promise implementations. What is new is that ES6/2015 has an implementation. This implementation is one of the ES6 features which is available out of the box in Node. You may have encountered the prior libraries, but we want to learn the new standard.
A promise is a container for a future value. A promise is an object which can only be in one of three states. It’s initial state is pending.
It’s state can change to either resolved or rejected, and once it changes from pending, it cannot change a second time.
As a consumer of a promise, you have two hooks: then and catch.
These two functions are your means to extract that future value when it becomes available.
Your handler will not be called until the promise moves out of the pending state.
If the promise is resolved, the handler given to then will be called.
If the promise is rejected, the handler given to catch will be called.
If the promise has already been resolved or rejected before you call then or catch, your handler will be called immediately.
As a promise producer, instead of taking a callback, you define a function to return a promise.
The promise constructor takes a function. The parameters to this function are resolve and reject. You call resolve or reject to move the promise out of the pending state.
In this case there is no blocking operation and the promise will just be for some structured error handling.
If the denominator is zero we reject the promise and indicate it was a divide by zero.
Otherwise, we call resolve with the answer
Alright, lets call our divide function. What do you think the result will be?
Show of hands:
Who thinks the result will be 5?
Who thinks the result will be ‘divide by zero’? <Ask why they though this>
The answer is 5 because after the promise has changed from the pending state, it’s value can never be changed.
Okay, how about calling divide with 10 and zero?
Yes ‘divide by zero’ is the answer
The syntax used here is the full complete syntax. It is the syntax you must use when performing a blocking action. However, there are some shorthand helper methods you can use in some scenarios too.
In our divide example, the answer is already known. There is no requirement that you can only use promises when there is an asynchronous operation involved.
If you are implementing a function where the answer can be obtained without a blocking operation, you can use some short hand notation.
You can use Promise’s resolve and reject functions directly.
I don’t know what your preferences are, but I’d much prefer to write this variant when I have the chance.
The key is that you cannot use them with blocking operations.
Okay, let’s take the theory and sample work we have done so far and begin to convert the race standings.
The first thing we need to do is create a way to convert from the mongoose callback style to something that returns a promise
First we need a helper method to convert our mongo queries to return promises. Note the real work happens in the callback
Next we need another promise function: all.
All takes an array of promises and returns a promise.
The returned promise is resolved when all the tasks resolve.
It could be rejected if one of the tasks are rejected.
We are now ready to solve callback hell with promises.
In case you have forgotten, here is our spec. Four queries and some work to process the results.
With this solution we no longer have the deeply nested callbacks and it is much easier to reason about the code. It looks much more like our spec
Personally, what I like about this version is how much easier it is to see the details of each query.
So far everything that we have looked at has involved a single then. However, it is not uncommon to need to do more than one thing to accomplish a task. So lets look at yet another way to use promises.
I admit. I’m a latecomer to BASH, but I’ve learned to love it’s penchant for solving problems with creating a pipeline of small operations.
You may not be familiar with bash. Just in case, here is what this line says.
*list all the files in the directory.
*filter out the lines that don’t contain Aug in them
*sort the remaining lines using the 5th column
*and finally page the results
Each one of these commands do a small part and put together do a complex task.
Often an asynchronous operation might be one of many that you need to perform in order to accomplish a task. One of the features I love about promises is how they support and encourage this pipelining of small tasks.
Here is a very simple example of chaining. An operation that produces a promise and then two tasks that operate on the promise value
Note that the first then returns plain old JavaScript. It does not return a promise. And that is just fine. This was not something that was immediately obvious to me, but it is very handy.
Here is a longer example, but note what happens in the middle, there is an operation that causes a promise to be rejected.
The third and the fourth then conditions are skipped and the handler to catch is executed.
Much like try/catch you can use this to really clean up your error handling.
If we wrote this code to use the previously defined divide function, we would alway have the promise rejected.
The handler you supply to then is only given one parameter when the promise is resolved.
What do you do when the next operation you need to perform needs more than one parameter? (closure, partial application)
To answer this question, lets take a side trip away from promises for a bit.
Here is a simple function. You all know what it’s going to do.
But what change do you make if I said that you needed come up with a way to always to add 5 to a number?
Maybe this is what you might do.
And it works too!
What if I said you needed to support adding 6 as well as 5. This solution quickly starts to become less DRY. It’s also very static. There are many times the data you bind is generated at runtime. In that case this method won’t work.
What we have here is a manual case of a functional programming technique called partial application. The good news is that there are libraries that can do the heavy lifting.
You could use the built in bind, but it is verbose and you can only bind the parameters on the left hand side. Which can be a problem.
Using lodash or ramda is my preferred solution. Both provide a partial function.
The first parameter for partial is the function you would like to overload. After that is a variable argument list. In this case we are only affixing one parameter, but there are no restrictions on the number. It could even be all of the parameters a function takes.
I mentioned that one downside of bind is that you can only bind the left hand parameter. X in this case. But what if the parameter you want to bind is on the right end of the parameter list?
This is the reason I like lodash and ramda. Not only do they provide a partial function, they also provide partialRight.
Here we have asked lodash to bind the value supplied to the right most parameter, or y in our case.
You might be thinking this… bear with me!
We started out this section on chaining by talking about a set of small bash commands piped together to make a powerful compound command.
Let’s put the things we’ve learned together…
Lets imagine that instead of resolving an array of people, you are reading a file containing people. After you get the data back, you need to apply a filter and the order the data. Not all that different from our bash command.
There are any number of ways to accomplish this task, but lets make use of lodash’s filter and sortBy functions. Note that the first parameter of both filter and sortBy is the collection to work with. That collection is the data coming from our promise. That means if we want to chain the filter and sortBy, we’ll have to use partialRight
Partial application can be used in many places, not just with chaining promises.
When you are testing your code, you often want to use dependency injection to insert some alternate behavior. In this case we do not want our readKids function to access the file system. So we use DI to override that in test scenarios.
Our mock resolves an empty array. But then in each test we can provide yet another implementation.
Using partial application makes for more compact and explicit code
Something important happened on September 8th.
Does anyone have a guess what that might be?
Node didn’t support arrow functions without flags until Node 4.
Now I know that there are nearly 8 weeks between Node 4 and now, but it wasn’t until last week that I had a revelation.
For months I’ve been complaining about this kind of code to my friend Darren. The meat of what is being done is kind of hidden at the end. Buried in boiler plate.
Somehow it takes more effort than I want to read it.
The _.partialRight is kind of boiler plate. In addition carries a level of implicit knowledge
Hands down in this example, arrow functions and closure wins. It is simpler and more explicit.
However, I still have times when I use _.partial application.
My point isn’t to say “Don’t use partial and partial right” Rather to say know both and know when to make use of each form.
I hope you’ll look to use both styles.
Short arrow functions are good. If they are more than two lines prefer named function expressions
If you have a good size process, it helps clarity to start with a promise, even if it’s not a blocking operation