Fluent asynchronous API 1: API requirements
Today I built a fluent asynchronous API. This is hard to do if you want the API to remain as close as possible to a synchronous API, but still maintain reliable and consistent asynchronous behavior. There are examples of this around (Erik Meijer’s reactive extensions come to mind), quite a few –not always clear– blog posts, a general functional pattern, and a few npm packages. None of this however seemed to correspond exactly to what I wanted.
First, I wanted the API to look as natural as possible, surfacing asynchronous idioms only when necessary. Second, I wanted the method definitions themselves to be easy to write and read, and even to look exactly like ordinary, synchronous method definitions. I also quickly realized that what I needed was a library that would help me write my API, taking care of the plumbing, rather than just a pattern that I would use to implement the API.
The closest thing I found was the fluid package, but that adopts a purely asynchronous programming model, and just makes that more palatable (which is a lot, don’t get me wrong).
Here’s an example of using an API that corresponds to my requirements. The dump object has two methods: write, which synchronously writes a string to the console, and fromFile, which asynchronously reads a file from disk, and dumps its contents to the console.
dump
.write('The file readme.txt contains:')
.fromFile('readme.txt')
.write('End of file...');
The output of this program looks like this:
The file readme.txt contains:
Lorem ipsum dolor sit amet
End of file...
As you can see, the output order is the same as the calling order, despite the fact that fromFile is asynchronous, and write is not. That means that the second write call at least needs to be transformed into an asynchronous method call, so that it can happen after the file read has called back.
Also notice that no callback function is present, as the method chaining gives enough information about the desired outcome. I am cheating a little however by only showing this fragment of code: because this chain of calls is asynchronous, despite the appearances, code that is written directly after it will be executed immediately, and not after the code is done executing. As a consequence, and until JavaScript gets async/await, we’ll need to be able to provide a callback function as a continuation of execution:
dump
.write('The file readme.txt contains:')
.fromFile('readme.txt')
.write('End of file...')
.then(function() {
// do something else
});
That’s it for today’s post. In the next one, I’ll show the implementation of the dump object itself. In the third post, I’ll show the utility library that I built to help building such fluent asynchronous APIs.