既然我们有ES6承诺,还有理由使用Q或BlueBird等承诺库吗?

时间:2016-01-23 07:35:02

标签: javascript node.js promise q bluebird

在Node.js添加了对promises的原生支持之后,还有理由使用像Q或BlueBird这样的库吗?

例如,如果你正在开始一个新项目,让我们假设在这个项目中你没有任何使用这些库的依赖项,我们可以说没有更多理由可以使用这些库吗?

1 个答案:

答案 0 :(得分:347)

古老的谚语说你应该为这份工作挑选合适的工具。 ES6承诺提供基础知识。如果你想要或需要的只是基础知识,那么应该/可以为你做好。但是,工具箱中有更多工具而不仅仅是基础知识,并且在某些情况下,这些附加工具非常有用。而且,我认为ES6的承诺甚至缺少一些像promisification这样的基础知识,这些基础知识在几乎每个node.js项目中都很有用。

我对[{3}}最熟悉,所以我主要根据我对该图书馆的经验说话。

所以,这是我使用功能更强大的Promise库的六大理由

  1. 非Promisified异步接口 - .promisify().promisifyAll()非常有用,可以处理仍需要简单回调的所有异步接口,并且不需要然后返回promises - 一行代码创建一个整个界面的promisified版本。

  2. 更快 - Bluebird比大多数环境中的本机承诺都要Bluebird promise library

  3. 异步数组迭代的排序 - Promise.mapSeries()Promise.reduce()允许您遍历数组,在每个元素上调用异步操作,但对异步进行排序操作使它们一个接一个地发生,而不是同时发生。您可以这样做,因为目标服务器需要它,或者您需要将一个结果传递给下一个。

  4. Polyfill - 如果您想在旧版浏览器客户端中使用promises,则无论如何都需要polyfill。也可以得到一个有能力的polyfill。由于node.js具有ES6承诺,因此您不需要在node.js中使用polyfill,但您可以在浏览器中使用。如果您对node.js服务器和客户端进行编码,那么在两者中都有相同的promise库和功能可能非常有用(更容易共享代码,环境之间的上下文切换,使用异步代码的常见编码技术等) ...)。

  5. 其他实用功能 - 蓝鸟有Promise.map()Promise.some()Promise.any()Promise.filter()Promise.each()和{ {1}}所有这些偶尔都很方便。虽然可以使用ES6承诺和附加代码执行这些操作,但Bluebird已经预先构建并预先测试了这些操作,因此使用它们更简单,代码更少。

  6. 内置警告和完整堆栈跟踪 - Bluebird有许多内置警告,可提醒您可能出现错误代码或错误的问题。例如,如果你调用一个在Promise.props()处理程序中创建新promise的函数而不返回该promise(将其链接到当前的promise链),那么在大多数情况下,这是一个偶然的错误,Bluebird会给出你警告这个效果。其他内置蓝鸟警告是significantly faster

  7. 以下是有关这些主题的更多细节:

    <强> PromisifyAll

    在任何node.js项目中,我立即使用Bluebird,因为我在.then()模块之类的标准node.js模块上使用了.promisifyAll()

    Node.js本身不为像fs模块那样执行异步IO的内置模块提供promise接口。因此,如果你想在这些接口上使用promises,你可以手工编写你使用的每个模块函数的promise包装器,或者获得一个可以为你做这个或者不使用promises的库。

    Bluebird的fsPromise.promisify()提供了node.js的自动包装,调用约定async API来返回promises。它非常有用且节省时间。我一直都在使用它。

    以下是一个如何运作的例子:

    Promise.promisifyAll()

    另一种方法是为您想要使用的每个const Promise = require('bluebird'); const fs = Promise.promisifyAll(require('fs')); fs.readFileAsync('somefile.text').then(function(data) { // do something with data here }); API手动创建自己的承诺包装器:

    fs

    并且,您必须为要使用的每个API函数手动执行此操作。这显然没有意义。它的样板代码。您也可以使用一个实用程序来为您工作。蓝鸟的const fs = require('fs'); function readFileAsync(file, options) { return new Promise(function(resolve, reject) { fs.readFile(file, options, function(err, data) { if (err) { reject(err); } else { resolve(data); } }); }); } readFileAsync('somefile.text').then(function(data) { // do something with data here }); Promise.promisify()就是这样的实用工具。

    其他实用功能

    以下是我特别认为有用的一些Bluebird功能(下面有一些关于如何节省代码或加速开发的代码示例):

    Promise.promisifyAll()

    除了有用的功能外,Promise.promisify() Promise.promisifyAll() Promise.map() Promise.reduce() Promise.mapSeries() Promise.delay() 还支持并发选项,允许您指定允许同时运行的操作数,这在您有很多事情要做时特别有用,但不能压倒一些外部资源。

    其中一些既可以被称为独立的,也可以用于承诺,它本身可以解析为可以节省大量代码的迭代。

    <强>填充工具

    在浏览器项目中,由于您通常仍希望支持某些不具备Promise支持的浏览器,因此无论如何您最终都需要使用polyfill。如果您还使用jQuery,您有时可以使用jQuery内置的promise支持(虽然在某些方面非常非标准,可能在jQuery 3.0中修复),但如果项目涉及任何重要的异步活动,我发现Bluebird中的扩展功能非常有用。

    <强>更快

    另外值得注意的是,Bluebird的承诺似乎明显快于V8内置的承诺。有关该主题的更多讨论,请参阅described here

    Big Thing Node.js缺失

    什么会让我考虑在node.js开发中使用Bluebird,如果node.js在promisify函数中构建,那么你可以这样做:

    Promise.map()

    或者只是提供已经有效的方法作为内置模块的一部分。

    在那之前,我是用Bluebird做的:

    const fs = requirep('fs');
    
    fs.readFileAsync('somefile.text').then(function(data) {
       // do something with data here
    });
    

    在node.js中内置ES6 promise支持并且没有任何内置模块返回promises似乎有点奇怪。这需要在node.js中进行整理。在那之前,我使用Bluebird来宣传整个图书馆。因此,感觉promish现在在node.js中实现了大约20%,因为没有任何内置模块可以让你使用promises,而无需先手动包装它们。

    实施例

    以下是普通Promises与Bluebird的promisify和const Promise = require('bluebird'); const fs = Promise.promisifyAll(require('fs')); fs.readFileAsync('somefile.text').then(function(data) { // do something with data here }); 的示例,用于并行读取一组文件并在完成所有数据时通知:

    平原承诺

    Promise.map()

    蓝鸟const files = ["file1.txt", "fileA.txt", "fileB.txt"]; const fs = require('fs'); // make promise version of fs.readFile() function fsReadFileP(file, options) { return new Promise(function(resolve, reject) { fs.readFile(file, options, function(err, data) { if (err) return reject(err); resolve(data); }); }); } Promise.all(files.map(fsReadFileP)).then(function(results) { // files data in results Array }, function(err) { // error here }); Promise.map()

    Promise.promisifyAll()

    以下是一个简单的Promises与Bluebird的promisify和const Promise = require('bluebird'); const fs = Promise.promisifyAll(require('fs')); const files = ["file1.txt", "fileA.txt", "fileB.txt"]; Promise.map(files, fs.readFileAsync).then(function(results) { // files data in results Array }, function(err) { // error here }); 的示例,当您从远程主机读取一堆URL时,您最多可以一次读取4个,但是想要保持尽可能多的并行请求:

    普通JS承诺

    Promise.map()

    蓝鸟承诺

    const request = require('request');
    const urls = [url1, url2, url3, url4, url5, ....];
    
    // make promisified version of request.get()
    function requestGetP(url) {
        return new Promise(function(resolve, reject) {
            request.get(url, function(err, data) {
                if (err) return reject(err);
                resolve(data);
            });
        });
    }
    
    function getURLs(urlArray, concurrentLimit) {
        var numInFlight = 0;
        var index = 0;
        var results = new Array(urlArray.length);
        return new Promise(function(resolve, reject) {
            function next() {
                // load more until concurrentLimit is reached or until we got to the last one
                while (numInFlight < concurrentLimit && index < urlArray.length) {
                    (function(i) {
                        requestGetP(urlArray[index++]).then(function(data) {
                            --numInFlight;
                            results[i] = data;
                            next();
                        }, function(err) {
                            reject(err);
                        });
                        ++numInFlight;
                    })(index);
                }
                // since we always call next() upon completion of a request, we can test here
                // to see if there was nothing left to do or finish
                if (numInFlight === 0 && index === urlArray.length) {
                    resolve(results);
                }
            }
            next();
        });
    }
    
相关问题