Node.js Asynchronous Library Comparison - Q vs Async

Jackson

I have used kriskowal's Q library for a project (web scraper / human-activity simulator) and have become acquainted with promises, returning them and resolving/rejecting them, and the library's basic asynchronous control flow methods and error-throwing/catching mechanisms have proven essential.

I have encountered some issues though. My promise.then calls and my callbacks have the uncanny tendency to form pyramids. Sometimes it's for scoping reasons, other times it's to guarantee a certain order of events. (I suppose I might be able to fix some of these problems by refactoring, but going forward I want to avoid "callback hell" altogether.)

Also, debugging is very frustrating. I spend a lot of time console.log-ing my way to the source of errors and bugs; after I finally find them I will start throwing errors there and catching them somewhere else with promise.finally, but the process of locating the errors in the first place is arduous.

Also, in my project, order matters. I need to do pretty much everything sequentially. Oftentimes I find myself generating arrays of functions that return promises and then chaining them to each other using Array.prototype.reduce, which I don't think I should have to do.

Here is an example of one of my methods that uses this reduction technique:

removeItem: function (itemId) {

  var removeRegexp = new RegExp('\\/stock\\.php\\?remove=' + itemId);

  return this.getPage('/stock.php')
  .then(function (webpage) {
    var
      pageCount = 5,
      promiseFunctions = [],
      promiseSequence;

    // Create an array of promise-yielding functions that can run sequentially.
    _.times(pageCount, function (i) {
      var promiseFunction = function () {
        var
          promise,
          path;

        if (i === 0) {
          promise = Q(webpage);
        } else {
          path = '/stock.php?p=' + i;
          promise = this.getPage(path);
        }

        return promise.then(function (webpage) {
          var
            removeMatch = webpage.match(removeRegexp),
            removePath;

          if (removeMatch !== null) {
            removePath = removeitemMatch[0];

            return this.getPage(removePath)
            .delay(1000)
            // Stop calling subsequent promises.
            .thenResolve(true);
          }

          // Don't stop calling subsequent promises.
          return false;

        }.bind(this));
      }.bind(this);

      promiseFunctions.push(promiseFunction);
    }, this);

    // Resolve the promises sequentially but stop early if the item is found.
    promiseSequence = promiseFunctions.reduce(function (soFar, promiseFunction, index) {
      return soFar.then(function (stop) {
        if (stop) {
          return true;
        } else {
          return Q.delay(1000).then(promiseFunction);
        }
      });
    }, Q());

    return promiseSequence;
  }.bind(this))
  .fail(function (onRejected) {
    console.log(onRejected);
  });
},

I have other methods that do basically the same thing but which are suffering from much worse indentation woes.

I'm considering refactoring my project using coalan's async library. It seems similar to Q, but I want to know exactly how they differ. The impression I am getting is that async more "callback-centric" while Q is "promise-centric".

Question: Given my problems and project requirements, what would I gain and/or lose by using async over Q? How do the libraries compare? (Particularly in terms of executing series of tasks sequentially and debugging/error-handling?)

Jackson

Both libraries are good. I have discovered that they serve separate purposes and can be used in tandem.

Q provides the developer with promise objects, which are future representations of values. Useful for time travelling.

Async provides the developer with asynchronous versions of control structures and aggregate operations.

An example from one attempt at a linter implementation demonstrates a potential unity among libraries:

function lint(files, callback) {

    // Function which returns a promise.
    var getMerged = merger('.jslintrc'),

        // Result objects to invoke callback with.
        results = [];

    async.each(files, function (file, callback) {
        fs.exists(file, function (exists) {

            // Future representation of the file's contents.
            var contentsPromise,

                // Future representation of JSLINT options from .jslintrc files.
                optionPromise;

            if (!exists) {
                callback();
                return;
            }

            contentsPromise = q.nfcall(fs.readFile, file, 'utf8');
            optionPromise = getMerged(path.dirname(file));

            // Parallelize IO operations.
            q.all([contentsPromise, optionPromise])
                .spread(function (contents, option) {
                    var success = JSLINT(contents, option),
                        errors,
                        fileResults;
                    if (!success) {
                        errors = JSLINT.data().errors;
                        fileResults = errors.reduce(function (soFar, error) {
                            if (error === null) {
                                return soFar;
                            }
                            return soFar.concat({
                                file: file,
                                error: error
                            });
                        }, []);
                        results = results.concat(fileResults);
                    }
                    process.nextTick(callback);
                })
                .catch(function (error) {
                    process.nextTick(function () {
                        callback(error);
                    });
                })
                .done();
        });
    }, function (error) {
        results = results.sort(function (a, b) {
            return a.file.charCodeAt(0) - b.file.charCodeAt(0);
        });
        callback(error, results);
    });
}

I want to do something potentially-blocking for each file. So async.each is the obvious choice. I can parallelize related operations per-iteration with q.all and reuse my option values if they apply to 2 or more files.

Here, Async and Q each influence the control flow of the program, and Q represents values resolving to file contents sometime in the future. The libraries work well together. One does not need to "choose one over the other".

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

async => TRUE in drupal_add_js() : asynchronous call for a js file

来自分类Dev

Node.JS中的ASYNC

来自分类Dev

Node.js Q承诺多个参数

来自分类Dev

在Node JS中限制Q许诺并发

来自分类Dev

Node.js Q调用多个参数

来自分类Dev

蓝鸟vs async.js性能

来自分类Dev

bluebird vs async.js performance

来自分类Dev

蓝鸟vs async.js性能

来自分类Dev

了解Node.JS async.parallel

来自分类Dev

Node.js中的async / await替换

来自分类Dev

node.js中的async.eachSeries

来自分类Dev

.net中的Node.js与Async / await

来自分类Dev

node.js嵌套async.eachSeries

来自分类Dev

Node.js:async.map变慢

来自分类Dev

Node JS Async Promise。所有问题

来自分类Dev

Node.js中的async / await替换

来自分类Dev

使用 Node js Await/Async 函数

来自分类Dev

与Node js async / await相比,了解Python async / await

来自分类Dev

Node.js异步库比较-Q与异步

来自分类Dev

Node.js中的异步和Q Promise

来自分类Dev

带有node.js的Q异步库

来自分类Dev

Node.js Q承诺forEach返回未定义

来自分类Dev

Q Promise Node.js如何循环解析

来自分类Dev

Node.js / Q:链接承诺按顺序

来自分类Dev

Node.js Q承诺then()链接不会等待

来自分类Dev

使用Q Promises在node.js中链接GET请求

来自分类Dev

Node.js Q承诺forEach返回未定义

来自分类Dev

Phonegap / Cordova构建android node_modules / q / q.js抛出e;

来自分类Dev

运行量角器时出错... node_modules / q / q.js:155