So, we are writing an application with a bunch of asynchronous requests. We need to send two asynchronous requests and process their result only after the result of both is received. For example, it can be asynchronous calls to a file and a request to the database, the results of which must be added together and processed. Or two agzhaks request.
But the peculiarity of asynchronous requests is that we do not know which of them will come first and which one will come last. Solve it in different ways, but I have not yet seen the beautiful and elegant. In the topic, I will tell you how I see it.
var process = processFsAndDb.after('fs', 'db'); asyncFsAccess( file, process.fs); asyncDbAccess(query, process.db);
Usually come in one of the following options:Maybe lucky
Typically, this can only be done by people who do not understand asynchronous calls at all. Something like this:
var fileData = null; asyncFsAccess( file, function (data) { fileData = data; }); asyncDbAccess(query, function (dbData) { processFsAndDb(fileData, dbData); });
Imagine that the file system was slowed down a bit and the request from the database came faster. Then nothing will be in fileData, although we just didn’t get this data! Sadly ...
Figs with him, wait
Another option is at least reliable. We make one request, we wait until it is completed, we make the second request.
asyncFsAccess( file, function (fileData) { asyncDbAccess(query, function (dbData) { processFsAndDb(fileData, dbData)
This is better than hope for the best, but here we are sacrificing time - the base could already search, while the file system is being accessed, but no - you have to sit with folded arms, wait. And, in the case of ajax, the user will wait for a response twice as long. Not good
')
Check 7 times
Flag system or variable checking. After each request we mark the flag corresponding to it, and if all flags are checked, we execute. the most correct option.
var fileData = dbData = null; asyncFsAccess( file, function (data) { fileData = data; if (fileData && dbData) processFsAndDb(fileData, dbData); }); asyncDbAccess(query, function (data) { dbData = data; if (fileData && dbData) processFsAndDb(fileData, dbData); });
But not very beautiful. The code is repeated, the flags must be added in many places. If you need to send three simultaneous requests - there will be a text
Check out 7 times
var fileData = dbData = null; var process = function () { if (fileData && dbData) processFsAndDb(fileData, dbData); }; asyncFsAccess( file, function (data) { fileData = data; process(); }); asyncDbAccess(query, function (data) { dbData = data; process(); });
This is better, but also not very pleasant. Flags are repeated 4 times (count the number of fileData variables)
My option is expanding the prototype
The code, as a result, is:
var process = function (args) .after('fs', 'db'); asyncFsAccess( file, process['fs']); asyncDbAccess(query, process['db']);
Or even:
var process = processFsAndDb.after('fs', 'db'); asyncFsAccess( file, process.fs); asyncDbAccess(query, process.db);
In fact, we will use the option with flags, but abstract it under the method that we add to the prototype of the function:
Function.prototype.after = function () { var onReady = this, after = {}, ready = {}; var checkReady = function () { for (var i in after) if (!(i in ready)) return; onReady(ready); }; for (var i = 0, l = arguments.length; i < l; i++) { (function (key) { after[key] = function () { ready[key] = arguments; checkReady(); }; })(arguments[i]); } return after; };
I think this is quite a convenient and beautiful way for such a rare but important task.