Contents tagged with Asynchronous
-
The sideways stack trace
A common pattern in asynchronous programming is the callback pattern. Libraries such as async make use of that pattern to build asynchronous versions of common control flow structures. One unfortunate possible consequence of this is strange stack traces. For instance, here is code that executes five functions serially:
var functions = [
function one(done) {done();},
function two(done) {done();},
function three(done) {done();},
function four(done) {done();},
function five(done) {done();}
];
async.series(functions, function final() {
debugger;
}); -
The curse of the never-ending async method call
Whether you use promises, callbacks, or other abstractions (except maybe the async/await pattern) to deal with asynchrony, control flow is going to get harder to follow. Stack traces are typically meaningless, and finding what went wrong can take substantially more time. One thing that very often goes wrong, especially during development, is when an asynchronous method never calls back. It may be because a service never answers, or because there’s a bug in the code that causes the callback to never be reached. No matter what the cause is, when that happens, you’re typically out of luck because by the time you notice the unusual delay, the execution engine has already left your code without leaving a trace. Breaking into the debugger will just lead you nowhere. You’ll have to tediously re-run and trace the execution of the code with lots of breakpoint into lots of callback functions. No fun.
-
It’s asynchronous all the way down
Captain Obvious has something to tell us:
Asynchronous methods can call synchronous ones, but synchronous methods can’t call asynchronous methods, or they must become asynchronous.
Duh.
-
Fluent asynchronous API 6: recursive calls
This can never work:
api
.then(function thenRegisterNextTick(next) {
process.nextTick(function onTick() {
api.finally(next);
});
})
.finally(done);The “next” callback function gets called from a promise the execution of which depends on the completion of the “then” call. This is a deadlock, albeit a subtle one.
-
Fluent asynchronous API 5: internally calling methods
One more thing about fluent asynchronous APIs… Because synchronous methods are changed into asynchronous ones by the flasync helper, internal calls of a synchronous method would in principle also have to be asynchronous calls. That would go against the requirements that I’ve set in the first post to provide a way for API authors to write code that is as close as possible to what they would write in a regular API.
-
Fluent asynchronous API 4: Nope, Q won’t work
As I said at the end of the previous post, I really thought that a promise library, Q in particular, would help, and would enable me to get rid of my helper library. It turns out, after I tried to make it work, that it won’t. The reasons have to do , among other things, with how hard it is to chain a new asynchronous method onto an existing promise, a strange limitation of those libraries. Well, in fact you can very well create a new promise that combines the old one and a new one for the new asynchronous operation, but that would mean managing a list of promises instead of managing the todo list of callbacks I have in my own helper. In other words, I would still have to maintain a helper that would be, maybe, marginally simpler than what I have now, except that it would require a dependency on another much more complex library that I didn’t write.
-
Fluent asynchronous API 3: the helper library
In this third post, I’ll show the inner workings of the little helper library that I wrote to build fluent asynchronous APIs. As you’ll see, it’s not simple, but analyzing it does, I hope, give interesting insights about asynchronous programming.
-
Fluent asynchronous API 2: building an API
In the first post of this series, I established my requirements for a fluent asynchronous API, and showed what using such an API looks like. In this post, I’ll show what the code for the API itself looks like. On this side of things, the requirements are that writing synchronous methods in such an asynchronous API should look as close as possible to writing the same methods in a synchronous API.
-
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.
-
Some asynchronous JavaScript weirdness
I feel a little silly about the time it took me to find that bug, but it’s a fairly typical one, so it’s worth sharing. While writing a test case for an asynchronous operation, execution of my code would seemingly magically skip over a whole bit of code and jump directly to the assertions at the end of my test case. It looked really bizarre, as if JavaScript didn’t want to run my test code, and I suspected a bug in the debugger for a brief moment. Of course, that was just me not spotting the obvious bug I had written a few minutes earlier. But let me show you some simplified code that demonstrates the issue...