Just like How to avoid "if" chains?, this question is about what should be done to keep asynchronous programs clean.
Asynchronous programming is a totally different paradigm from procedural programming, and its really fun when you get your head around it. However, it can get messy when you have several asynchronous calls.
Consider this example (This is an abstraction of a real world scenario I encountered):
- Read file
a.txt
- Submit contents of
a.txt
to serverX
for processing and retrieve the output. - Read file
b.txt
. - Send contents of
b.txt
, and the response from serverX
(step 2) to serverY
, and get the output. - Save the response from server
Y
(step 4) toc.txt
.
With asynchronous calls, my JavaScript looks similar to this (function names are made up. All functions are asynchronous calls with callbacks. Error parameters are omitted to improve clarity. Real code with error parameters and error handling is a lot messier than this):
readFile('a.txt', function (file_a_data) {
requestToServer('http://xserver.com/', file_a_data, function (server_x_response) {
readFile('b.txt', function (file_b_data) {
var request_params = server_x_response + file_b_data;
requestToServer('http://yserver.com/', request_params, function (server_y_reponse) {
writeFile('c.txt', server_y_response);
});
});
});
});
As you can see, there is already four levels of indentation, and an arrow of code is building up. How can we avoid this nesting of callbacks, and write some clean code?
What I have tried so far:
One way of doing this would be to write synchronous, no-callback versions of the functions, whenever possible. A value is returned and on error exception is thrown.
try { var file_content = readFile('a.txt'); // do stuff with file_content } catch (ex) { // an error occured }
But there are few problems with this:
- For I/O heavy programs like this, the performance hit is very high.
- Elementary API functions (such as XmlHttpReqeust, or Node.js File System API already have callbacks. They are the asynchronous parts, and there is little we can do other than writing thin, synchronous wrappers.
Making all callbacks named functions and just specifying the function name for the callback:
function processFileA(data) { requestToServer('http://xserver.com/', file_a_data, processRequestX); } function processRequestX(response) { readFile('b.txt', function (file_b_data) { var request_params = server_x_response + file_b_data; requestToServer('http://yserver.com/', request_params, processRequestY); }); } function processRequestY(response) { writeFile('c.txt', server_y_response); } readFile('a.txt', processFileA);
This is similar to How to avoid hard-coded, chained asynchronous functions in Javascript/jQuery? This question looks like the inversion of my problem. The coding style of this question was one remedy I used to stop chains, but it does not look very nice. I think it looks like thin wrappers and spaghetti:
how to avoid callback chains?: A similar question (in title), but this is not really about asynchronous callbacks. This is about passing functions as parameters.