简体   繁体   English

Node.js 异步库比较 - Q vs Async

[英]Node.js Asynchronous Library Comparison - Q vs Async

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.我在一个项目(网络爬虫/人类活动模拟器)中使用了kriskowal 的 Q 库,并且已经熟悉了 promise,返回它们并解决/拒绝它们,并且该库的基本异步控制流方法和错误抛出/捕获机制已经证明基本的。

I have encountered some issues though.虽然我遇到了一些问题。 My promise.then calls and my callbacks have the uncanny tendency to form pyramids.我的promise.then调用和回调具有形成金字塔的不可思议的趋势。 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;我花了很多时间在console.log查找错误和错误的来源; 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.在我最终找到它们之后,我将开始在那里抛出错误并使用promise.finally将它们捕获到其他地方,但是首先定位错误的过程是艰巨的。

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.我经常发现自己生成返回承诺的函数数组,然后使用Array.prototype.reduce将它们相互链接,我认为我不应该这样做。

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 .我正在考虑使用coalan 的异步库重构我的项目。 It seems similar to Q, but I want to know exactly how they differ.它似乎与 Q 相似,但我想知道它们究竟有何不同。 The impression I am getting is that async more "callback-centric" while Q is "promise-centric".我得到的印象是 async 更“以回调为中心”,而 Q 则“以承诺为中心”。

Question: Given my problems and project requirements, what would I gain and/or lose by using async over Q?问题:鉴于我的问题和项目要求,通过 Q 使用异步我会得到什么和/或失去什么? How do the libraries compare?图书馆如何比较? (Particularly in terms of executing series of tasks sequentially and debugging/error-handling?) (特别是在顺序执行一系列任务和调试/错误处理方面?)

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. Q 为开发人员提供了 promise 对象,它们是值的未来表示。 Useful for time travelling.对时间旅行很有用。

Async provides the developer with asynchronous versions of control structures and aggregate operations. Async 为开发人员提供了异步版本的控制结构和聚合操作。

An example from one attempt at a linter implementation demonstrates a potential unity among libraries:一个来自 linter 实现尝试的示例展示了库之间的潜在统一性:

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.所以async.each是显而易见的选择。 I can parallelize related operations per-iteration with q.all and reuse my option values if they apply to 2 or more files.我可以将每次迭代的相关操作与q.all并行q.all用我的选项值(如果它们适用于 2 个或更多文件)。

Here, Async and Q each influence the control flow of the program, and Q represents values resolving to file contents sometime in the future.这里,Async 和 Q 各自影响程序的控制流,Q 表示将来某个时间解析为文件内容的值。 The libraries work well together.这些库可以很好地协同工作。 One does not need to "choose one over the other".一个人不需要“选择一个而不是另一个”。

Callback pyramids in your code can be simplified using promise composition and javascript lexical scoping.可以使用 promise 组合和 javascript 词法范围来简化代码中的回调金字塔。

removeItem: function (itemId) {

  var removeRegexp = new RegExp('\\/stock\\.php\\?remove=' + itemId);
  var found = false
  var promise = getPage('/sock.php')

  _.times(5, (i) => {
    promise = promise.then((webpage) => {
      if (found) return true
      var removeMatch = webpage.match(removeRegexp)
      var found = removeMath !== null
      var nextPage = found ? removeMatch[0] : '/stock.php?p='+i+1
      return Q.delay(1000).then(() => this.getPage(nextPage))
    })
  })

  return promise.fail(console.log.bind(console))

},

IMHO async should not be used in new javascript code.恕我直言, async不应在新的 javascript 代码中使用。 Promises are more composable, and allow for a lot more intutive code. Promise 的可组合性更强,并允许使用更直观的代码。

The primary reason why node did not use promises was because of performance concerns which have largely been addressed very well by libraries like Bluebird and Q. node 不使用 promise 的主要原因是性能问题,Bluebird 和 Q 等库在很大程度上已经很好地解决了这些问题。

As async/await syntax becomes more mainstream, promises will pave the way for code that looks very similar with synchronous code.随着 async/await 语法变得越来越主流,promise 将为看起来与同步代码非常相似的代码铺平道路。

While this is still not an actual answer to my question (Q vs async), regarding my problem , I've found Selenium / WebDriverJs to be a viable solution.虽然这仍然不是我的问题(Q vs async)的实际答案,但关于我的问题,我发现 Selenium / WebDriverJs是一个可行的解决方案。

driver.get('http://www.google.com');
driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');
driver.findElement(webdriver.By.name('btnG')).click();
driver.wait(function() {
  return driver.getTitle().then(function(title) {
    return title === 'webdriver - Google Search';
  });
}, 1000);

WebDriver uses a queue to execute promises sequentially, which helps immensely with controlling indentation. WebDriver 使用队列顺序执行 promise,这对控制缩进有很大帮助。 Its promises are also compatible with Q's.它的承诺也与 Q 兼容。

Creating a sequence of promises is no longer an issue.创建一系列 Promise 不再是问题。 A simple for-loop will do.一个简单的 for 循环就可以了。

As for stopping early in a sequence, don't do this.至于在序列中提前停止,请不要这样做。 Instead of using a sequence, use an asynchronous-while design and branch.不使用序列,而是使用异步 while 设计和分支。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM