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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22493723/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 23:16:12  来源:igfitidea点击:

Node.js Asynchronous Library Comparison - Q vs Async

javascriptnode.jsasynchronousq

提问by Hymanson

I have used kriskowal's Q libraryfor 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.thencalls 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.)

虽然我遇到了一些问题。我的promise.then电话和回电有形成金字塔的不可思议的趋势。有时是为了范围的原因,有时是为了保证一定的事件顺序。(我想我也许可以通过重构来解决其中的一些问题,但我想完全避免“回调地狱”。)

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.

此外,调试非常令人沮丧。我花了很多时间console.log去寻找错误和错误的来源;在我最终找到它们之后,我将开始在那里抛出错误并用 捕捉它们的其他地方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.

此外,在我的项目中,订单很重要。我需要按顺序做几乎所有的事情。我经常发现自己生成了返回 promise 的函数数组,然后使用 将它们相互链接起来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. 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".

我正在考虑使用coalan 的异步库重构我的项目。它似乎与 Q 相似,但我想知道它们究竟有何不同。我得到的印象是 async 更“以回调为中心”,而 Q 则“以承诺为中心”。

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?)

问题:鉴于我的问题和项目要求,通过 Q 使用异步我会获得和/或失去什么?图书馆如何比较?(特别是在顺序执行一系列任务和调试/错误处理方面?)

采纳答案by Hymanson

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.

Q 为开发人员提供了 promise 对象,它们是值的未来表示。对时间旅行很有用。

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.eachis the obvious choice. I can parallelize relatedoperations per-iterationwith q.alland reuse my option values if they apply to 2 or more files.

我想为每个文件做一些潜在的阻塞。async.each显而易见的选择也是如此。如果它们适用于 2 个或更多文件,我可以将每次迭代的相关操作与我的选项值并行化并重q.all它们。

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".

这里,Async 和 Q 各自影响程序的控制流,Q 表示将来某个时间解析为文件内容的值。这些库可以很好地协同工作。一个人不需要“选择一个而不是另一个”。

回答by lorefnon

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 asyncshould not be used in new javascript code. Promises are more composable, and allow for a lot more intutive code.

恕我直言,async不应在新的 javascript 代码中使用。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 不使用 promises 的主要原因是性能问题,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 将为看起来与同步代码非常相似的代码铺平道路。

回答by Hymanson

While this is still not an actual answer to my question(Q vs async), regarding my problem, I've found Selenium / WebDriverJsto 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. Its promises are also compatible with Q's.

WebDriver 使用队列顺序执行 promise,这对控制缩进有很大帮助。它的承诺也与 Q 兼容。

Creating a sequence of promises is no longer an issue. A simple for-loop will do.

创建一系列 Promise 不再是问题。一个简单的 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 设计和分支。