Skip to content

Instantly share code, notes, and snippets.

@rsp
Last active November 11, 2018 15:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rsp/008fc3d761ccb16086d0 to your computer and use it in GitHub Desktop.
Save rsp/008fc3d761ccb16086d0 to your computer and use it in GitHub Desktop.
I/O API of My Dreams

I/O API of My Dreams

by Rafał Pocztarski

(This gist should be called "An asynchronous file and stream I/O API of my dreams for Node.js and a postulate to never mix concepts perfect for the events abstraction with concepts perfect for the promises abstraction, because those are non-overlapping magisteria never to be confused or interchanged" but it just doesn't sound as good.)

(See also a newer post: Promise You Call Me Back for some background.)

Let's say that I open a file for writing and everything is fine. I start writing like there was no tomorrow and then suddenly I fill the entire disk and get an error. I can handle it by truncating the file or not, or maybe deleting some other files but even though I got an error I still might be able to do something else later. The point is that after some errors I don't have to give up all my hope. The error is saying: Your stream has some problems. But it still exists.

Now, let's say that I am opening a file for reading but there is no such file. Now I get an error that basically says that I can forget about doing anything else with the thing that gave me that error. Ever. I may try to open that file in the future and it might exist then, but there is no point in keeping a reference to the object that gave me that error and keep checking if it might be ok now. The error is saying: There is no stream. And never will. Forget about it. Get over it.

So, when I create a new stream to read some file, what I am really interested in is this:

  1. I want to "connect" with a file /foo/bar.baz
  2. Is it ok? Do I have the connection?
  3. I want to read something. Call me when you have some data.
  4. Did I get some data? Was there any problem?

Now, the number 3 is something that I may do repeatedly. Number 4 is the result of what I'm doing and some other state of the world and may change in time. I might get called many times about new data or new problems. I want some events to which I could listen.

But the number 1 is done once and forever. And so is number 2. The stream will transition only once between the state of "Let me see if I can get you that file" and either "Ok, you got it" or "Sorry, I can't help." What I am really interested in is not the transition itself, but the final state. Yeah, I want to know it as soon as possible to not waste any time waiting, but if I ask after the transition already took place I don't want to hear: "I'll call you when the stream starts being open - but since it's already open and will never start being open again, I will never call you and you will die waiting." The same goes for the failure of opening the stream. I want to be able to check whenever I want to see if it is ok or not, if I got the connection as promised.

This is really the same as what I always hated in the onload events in the DOM. Yeah, it's great that I can be notified as soon as everything loads. But I don't want to hear: too bad it's already loaded so I guess I will just acknowledge that you'r waiting and never tell you that.

My point is that events are not a good abstraction for things that happen only once and stay in that state forever, because you are often interested not only in the transition but in the state itself.

A perfect abstraction for the DOM ready and onload events would be a promise, not an event. A promise gets fulfilled or rejected only once, or it can be too early to tell but once it's done, it's done.

A perfect abstraction for a stream in Node would be a promise to get an open stream, but once the promise gets resolved then it is either an open stream, or cannot be open and never will. And I want to be able to know it anytime I want, not only if I happen to listen in the exact moment when it happens.

The I/O is just an example of a more general idea. I want to have events for one things and promises for other things. The things that are perfect for events will not work well with promises. The things that are perfect for promises will not work well with events.

What is perfect for events? Things that can happen and call me many times and you never know if you get called again. Examples are: clicks, inbound network connections, new data on streams.

What is perfect for promises? Things that can happen once (either fail or succeed) and then stay that way forever. Examples are: responses to outbound network connections, opening files, having the DOM ready.

Please don't confuse them. Ever. Best wishes, the undersigned.

Short URL: https://bit.ly/dreamapi
Twitter cut'n'paste: I/O API of My Dreams by @pocztarski https://bit.ly/dreamapi
Spread the word:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment