Greane Tree Technology Group, LLC

Handle Dependencies with Node.js Async.js

Authored by on Wednesday, Feb 11, 2015

custom software development

If you've worked in the custom software development space for any amount of time you've probably run into the situation where you needed to resolve several dependencies in order to fulfill a domain requirement. In synchronous (blocking) software development you would write instructions in a linear fashion, fulfilling dependencies successively before writing the domain requirement. However, things aren't that straightforward in asychronous (non-blocking) software development, such as Node.js or io.js. In this article we're going to take a look at how to manage functional dependencies in server-side JavaScript.

Synchronous Software Development

Examine the following code fragment (I realize that JavaScript engines are non-blocking but pretend that the following is running in a blocking environment).

Overall, the flow of the business logic is clear. You can trace how the JavaScript engine will evaluate the getUserDashboardData function upon invocation. Since the database read calls are blocking, dependencies are obtained one after the other in a sequential fashion. This is much different compared to asynchronous software development.

Asynchronous Software Development

Again, examine the following code fragment.

Not only does this suffer from callback hell (yes, there are ways to mitigate this) but the flow of the business logic is layered in event handling, which is not straightforward. Unfortunately, there's a fair amount of JavaScript code like this, both on the client and server. Code written in this manner can be refactored to improve the following aspects:

  • Cleanliness
  • Composability
  • Maintainability
  • Readability
  • Testablity

If we utilize a JavaScript library called Async.js we could improve all of the above aspects.

Refactor using Async.js auto

Within the  Async.js library there is a method called auto which accepts an object literal containing keys and methods. The auto method will resolve all dependencies before calling the target method and do this for all methods that need to be executed (with the exception of an error condition). The following quote is from theAsync.js documenation:

"Determines the best order for running the functions in tasks, based on their requirements. Each function can optionally depend on other functions being completed first, and each function is run as soon as its requirements are satisfied."

And yet again, examine the following code fragment.

With the aforementioned refactor we accomplished quite a bit. Let's review the return on the effort.

Code Cleanliness

Before the refactor we had a rather obfuscated getUserDashboardData method. It was hard to follow what method calls depended on each other, and if a method call really depended on another at all. After the refactor we have a getUserDashboardData method that is mostly declarative. The auto method is responsible for determining the order in which to call dependant methods.

Code Composability

Before the refactor our dependant methods had varying method signatures. After the refactor we now have dependant methods that take the same type and number of parameters. These methods could be placed in a library and reused for other invocations of auto without the requirement of modifying said methods.

Code Maintainability

Before the refactor the getUserDashboardData method it was becoming unruly, and additions to the method would have made its condition worse. If we want to add more data to the user dashboard it's simply one more line of code in the getUserDashboardData method.

Code Readability

Along the same lines as Code Cleanliness, the declarative style of the auto method makes it simple to understand. With the addition of the processResults method, each method has a single responsibility and well abstracted.

Code Testability

All of the methods in the refactor are "pure" methods, which means that they are idempotent and without side effects. Mocking and stubbing depenencies are incredibly simple. We can pass in the mocks or stubs as parameters to these methods and test that they've been called correctly.

Conclusion

Using the Async.js auto method to declaratively handle dependant method call chains couldn't be easier and it's return on value for Clean Code is excellent. If you aren't using this method perhaps you should give it a try.

Questions, Comments, or Concerns?

Join the conversation on Twitter #asyncjs.