Let's say I have a function that looks like this:
var foo = function(callback) {
var final = {};
asyncFuncOne(function(x) {
final.x = x;
});
asyncFuncTwo(function(y) {
final.y = y;
});
callback(final);
});
Obviously, this doesn't do what I want it to do (call callback
on final
when it has both x and y). I have several questions:
- Is there a way to do what I want it to do without nesting everything?
- Does the current form introduce a race condition? Are both async functions accessing the same
final
?
One pretty bad idea, but I've had to use it before, because I wasn't about to import a 50k promise library for a single function, would be to set a looping Timeout that checks to see if all the required variables are set, and then calls the callback.
Approach #0. Painful life without promises. Yet life
Actually, your code like cries to be rewritten in promises. Trust me, this refactoring is something you 100% need. But ok, let's try to solve this particular problem without invoking promises at all - just as an exercise. Actually before the promise era the pattern was to introduce a special function that checks whether we can consider that we are done or not.
In your particular case such function is:
Then we can introduce asyncFuncDecorator:
With this two functions introduced you can write something like:
You can keep working on making this approach more flexible and universal but, once again, trust me, you'll end up with something very similar to promises, so better promises ;)
Approach #1. Promisifying existing functions
If, for some reason, you are not ready to rewrite all you functions from callback style to promises, you can promisify existing functions by using, once again, a decorator. Here's how it can be done for native Promises, which are present in all modern browsers already (for alternatives, check this question):
In that case you can rewrite you code in this fashion:
Not to say that actually foo it's better to be promisified itself ;)
Approach #2. Promises everywhere. From the very beginning
It worth to reiterate this thought - as soon as you need to trigger some function after N other async functions should be completed - promises in 99% cases are unbeatable. It almost always worth trying to rewrite existing code to in promise-based style. Here's how can such code look like
See how much better it become. Also, a common mistake of using promises - is to have a sequential waterfall of thens - retrieving first chunk of data, only after that - the second one, after that - the third one. You actually never should do this unless you are transforming data received in Nth request depending on what you've got in one of your previous requests - instead just use all method.
This is very crucial to understand. This is one of main reasons why promises quite often are misunderstood as something excessively complicated.
Sidenote: as of December'14, native Promises are natively supported by all major modern browsers except IE, and in Node.js has native promise support is a thing since version 0.11.13, so in real-life you still most probably will need to use promise library. There's a lot of Promise spec implementations, you can check this page for the list of standalone promise libraries, it's quite big, the most popular solutiona are, I guess, Q and bluebird.
Approach #3. Generators. Our bright future. Well, may be
This is something worth to mention, generators are de-facto supported in Firefox, Chromium-based browsers and node.js (called with --harmony_generators option). So, de-facto, there are cases when generators can be used, and actually are already used, in production code. It's just that if you are writing a general-purpose web app, you should be aware of this approach but you'll probably won't use it for a while. So, you can use the fact that generators in js allow you to invoke two-way communication through yield/iterator.next(). In that case.
Actually, there are already dozens of libraries which do this generator wrapping job for you. You can read more about this approach and related libraries here.
Another solution is to create a setter:
final.x
andfinal.y
are set on final, but after it's sent tocallback
so, unless the callback is waiting,x
andy
are undefined when callback receives them.You could check to see if one has come back in the response of the others and call out to the callback:
You could nest your callbacks, though this will cause
asyncfuncTwo
to not be called untilasyncfuncOne
has finished):Then there are Promises. These are the future of async however they are not fully supported across all browsers (namely, all of IE [11 and below at the this time]). In fact, 40% of all browser users are not using a browser that natively supports Promises. This means you will have to use a polyfill library to give you support adding substantial filesize to your page. For this simple problem and at this given time I wouldn't recommend using Promises for this simple issue. However, you should definitely read up on how they are used.
If you want to see what that could look like, it'd be this:
Note no callbacks, just use of
then
. In a real scenario you would also usecatch
andreject
. Read more about Promises here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise but, again, I personally don't see a strong need to use them for this single, specific issue (but, to each their own).