Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

task.js generators/promises vs. async callbacks

I want to know which of the two methods are better when dealing with asynchronous code in JavaScript. I want to understand which method leads to cleaner code. I'm used with promises and they seem more flexible than the async approach (https://github.com/caolan/async).

I also know about the Task.js library (http://taskjs.org/), but this relies on the yield keyword which is part of Ecmascript Harmony.

like image 586
Daniel Uzunu Avatar asked Mar 19 '13 00:03

Daniel Uzunu


People also ask

Are promises better than callbacks?

They can handle multiple asynchronous operations easily and provide better error handling than callbacks and events. In other words also, we may say that, promises are the ideal choice for handling multiple callbacks at the same time, thus avoiding the undesired callback hell situation.

Which is better promises or async await?

Async/Await is used to work with promises in asynchronous functions. It is basically syntactic sugar for promises. It is just a wrapper to restyle code and make promises easier to read and use. It makes asynchronous code look more like synchronous/procedural code, which is easier to understand.

What is difference between callback and promises in JS?

A callback function is passed as an argument to another function whereas Promise is something that is achieved or completed in the future. In JavaScript, a promise is an object and we use the promise constructor to initialize a promise.

Is async await implemented with generators?

In this article I will apply those concepts and show how we can use generators to build something similar to async/await. In fact, async/await is implemented using generators and promises.


3 Answers

The async library encapsulates a couple of very common asynchronous patterns, including making arbitrary async calls in parallel and iterating over a list asynchronously. It's designed to work with "nodeback" (err, res) APIs, which makes it useful for a lot of Node.js applications. async is however a specific solution, and it only simplifies the async patterns included in the library.

Promises, by contrast, are in my opinion a much more general solution to the problem of asynchronous code. Not only do they provide the obvious benefits at first glance of error-bubbling and of flattening callback pyramids, problems that would otherwise require the sorts of complex patterns async encapsulates can be solved much more simply.

I'll demonstrate this with a quick tour through some of async's available patterns. For instance, the async.waterfall function is used something like this:

async.waterfall([
      function (cb) {
        asyncCall('argument', cb);
      },
      function(resultOfFirstCall, cb) {
        anotherCall(resultOfFirstCall, 'someOtherArgument' cb);
      },
    ], function(err, res) {
      if (err) handle(err);
      useFinalResult(res);
    });

There's no equivalent to async.waterfall in most promise libraries (or at least there isn't one in Q), because it's so simple to implement it from scratch using Array.reduce, like so (example based on Q but pretty much the same on other promise libraries):

[
  function() {
    return asyncCall('argument');
  },
  function(resultOfFirstCall) {
    return anotherCall(resultOfFirstCall, 'someOtherArgument');
  }
].reduce(Q.when, Q())
 .then(useFinalResult, handle);

The other big functions in async include async.parallel, which Q includes as Q.all:

// async
async.parallel([
    asyncFunc,
    asyncFunc2
    ], function(err, res) {
      if (err) handle(err);
      useFinalResult(res);
      // res[0] === asyncFuncResult
      // res[1] === asyncFunc2Result
    });
// Q
Q.all([
  asyncFunc(),
  asyncFunc2()
]).then(useFinalResult, handle);

And async.map. You actually don't need async.map when you're using promises, because the normal Array.map is sufficient:

// async
async.map(['file', 'file2', 'file3'], fs.stat, function(err, res) {
  if (err) handle(err);
  useFinalResult(res);
});
// Q
Q.all(['file', 'file2', 'file3']
  .map(Q.nfbind(fs.stat)))
  .then(useFinalResult, handle);

The rest of async is similarly easy to implement concisely, using relatively simple pieces of your promise library. (Note that that last example used a function Q.nfbind: nfbind and the other nf* functions Q provides are basically all you need to use promises with nodeback APIs, so there isn't even a particularly big impedance trying to use promises with libraries that expect nodebacks.)

In the end, whether you use promises or nodebacks is up to you, but I think promises are a much more flexible, capable, and generally concise way to implement most all asynchronous operations.

Callbacks are imperative, promises are functional is worth a read for more information in this general vein.

like image 136
00dani Avatar answered Oct 17 '22 17:10

00dani


Since you tagged your question with node, I'd recommend the async library. The control flow functions are great to work with and eliminate the ugly and hard to follow callback chains. The API is setup really nice for injecting callbacks that follow node's signature (error, result) into the control functions. It's basically included by default in almost all node scripts I write.

While you can use async for client-side as well, it's probably unnecessary for most projects. jQuery includes promises, and you can accomplish the same thing with them.

like image 36
Andrew Kirkegaard Avatar answered Oct 17 '22 18:10

Andrew Kirkegaard


I think promise/a and async lib with difference targets, promise focus on one step async operation progress, and async focus on multi step async operate, for node, async has a wilder use for a lot of async apis.

by the way, to deal with async operations, use named function instead of Anonymous functions will be the most effective way

like image 1
Torrent Lee Avatar answered Oct 17 '22 18:10

Torrent Lee