既然我们有 ES6 承诺,还有理由使用像 Q 或 BlueBird 这样的承诺库吗? [英] Are there still reasons to use promise libraries like Q or BlueBird now that we have ES6 promises?

查看:15
本文介绍了既然我们有 ES6 承诺,还有理由使用像 Q 或 BlueBird 这样的承诺库吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Node.js 添加对 promise 的原生支持后,还有理由使用 Q 或 BlueBird 之类的库吗?

After Node.js added native support for promises, are there still reasons to use libraries like Q or BlueBird?

例如,如果您正在启动一个新项目,并且假设在该项目中您没有任何使用这些库的依赖项,我们是否可以说真的没有更多理由使用这些库?

For example if you are starting a new project and let's assume in this project you don't have any dependencies that use these libraries, can we say that there are really no more reasons to use such libraries?

推荐答案

古老的格言说,您应该为工作选择正确的工具.ES6 承诺提供了基础知识.如果您想要或需要的只是基础知识,那么这应该/可以为您工作.但是,工具箱中的工具不仅仅是基础工具,而且在某些情况下,这些附加工具非常有用.而且,我认为 ES6 的 promise 甚至缺少一些基础知识,例如在几乎每个 node.js 项目中都有用的 promisification.

The old adage goes that you should pick the right tool for the job. ES6 promises provide the basics. If all you ever want or need is the basics, then that should/could work just fine for you. But, there are more tools in the tool bin than just the basics and there are situations where those additional tools are very useful. And, I'd argue that ES6 promises are even missing some of the basics like promisification that are useful in pretty much every node.js project.

我最熟悉 Bluebird promise 库,所以我将主要讲根据我对该库的经验.

I'm most familiar with the Bluebird promise library so I'll speak mostly from my experience with that library.

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

  1. Non-Promisified 异步接口 - .promisify().promisifyAll() 对于处理所有这些异步非常有用仍然需要普通回调并且尚未返回承诺的接口 - 一行代码创建了整个接口的承诺版本.

  1. Non-Promisified async interfaces - .promisify() and .promisifyAll() are incredibly useful to handle all those async interfaces that still require plain callbacks and don't yet return promises - one line of code creates a promisified version of an entire interface.

更快 - Bluebird .

Faster - Bluebird is significantly faster than native promises in most environments.

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

Sequencing of async array iteration - Promise.mapSeries() or Promise.reduce() allow you to iterate through an array, calling an async operation on each element, but sequencing the async operations so they happen one after another, not all at the same time. You can do this either because the destination server requires it or because you need to pass one result to the next.

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

Polyfill - If you want to use promises in older versions of browser clients, you will need a polyfill anyway. May as well get a capable polyfill. Since node.js has ES6 promises, you don't need a polyfill in node.js, but you may in a browser. If you're coding both node.js server and client, it may be very useful to have the same promise library and features in both (easier to share code, context switch between environments, use common coding techniques for async code, etc...).

其他有用的功能 - Bluebird 有 Promise.map()Promise.some()Promise.any()Promise.filter()Promise.each()Promise.props() 都是偶尔得心应手.虽然这些操作可以使用 ES6 承诺和附加代码来执行,但 Bluebird 附带这些操作已经预先构建和预先测试,因此使用它们更简单,代码更少.

Other Useful Features - Bluebird has Promise.map(), Promise.some(), Promise.any(), Promise.filter(), Promise.each() and Promise.props() all of which are occasionally handy. While these operations can be performed with ES6 promises and additional code, Bluebird comes with these operations already pre-built and pre-tested so it's simpler and less code to use them.

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

Built in Warnings and Full Stack Traces - Bluebird has a number of built in warnings that alert you to issues that are probably wrong code or a bug. For example, if you call a function that creates a new promise inside a .then() handler without returning that promise (to link it into the current promise chain), then in most cases, that is an accidental bug and Bluebird will give you a warning to that effect. Other built-in Bluebird warnings are described here.

以下是有关这些不同主题的更多详细信息:

Here's some more detail on these various topics:

PromisifyAll

在任何 node.js 项目中,我都会立即在任何地方使用 Bluebird,因为我在标准 node.js 模块(例如 fs 模块)上大量使用了 .promisifyAll().

In any node.js project, I immediately use Bluebird everywhere because I use .promisifyAll() a lot on standard node.js modules like the fs module.

Node.js 本身并不像 fs 模块那样为执行异步 IO 的内置模块提供承诺接口.因此,如果您想在这些接口中使用 Promise,您要么为您使用的每个模块函数手动编写一个 Promise 包装器,要么获得一个可以为您执行此操作的库,或者不使用 Promise.

Node.js does not itself provide a promise interface to the built-in modules that do async IO like the fs module. So, if you want to use promises with those interfaces you are left to either hand code a promise wrapper around each module function you use or get a library that can do that for you or not use promises.

Bluebird 的 Promise.promisify()Promise.promisifyAll() 提供自动包装 node.js 调用约定异步 API 以返回承诺.这是非常有用和节省时间的.我一直在用.

Bluebird's Promise.promisify() and Promise.promisifyAll() provide an automatic wrapping of node.js calling convention async APIs to return promises. It's extremely useful and time saving. I use it all the time.

这是一个工作原理示例:

Here's an example of how that works:

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));

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

另一种方法是为您想要使用的每个 fs API 手动创建您自己的承诺包装器:

The alternative would be to manually create your own promise wrapper for each fs API you wanted to use:

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
});

而且,您必须为要使用的每个 API 函数手动执行此操作.这显然没有意义.这是样板代码.您不妨得到一个实用程序来为您完成这项工作.Bluebird 的 Promise.promisify()Promise.promisifyAll() 就是这样一个实用工具.

And, you have to manually do this for each API function you want to use. This clearly doesn't make sense. It's boilerplate code. You might as well get a utility that does this work for you. Bluebird's Promise.promisify() and Promise.promisifyAll() are such a utility.

其他有用的功能

以下是我特别觉得有用的一些 Bluebird 功能(下面有几个代码示例,介绍了这些功能如何节省代码或加快开发速度):

Here are some of the Bluebird features that I specifically find useful (there are a couple code examples below on how these can save code or speed development):

Promise.promisify()
Promise.promisifyAll()
Promise.map()
Promise.reduce()
Promise.mapSeries()
Promise.delay()

除了有用的功能外,Promise.map() 还支持并发选项,让您指定应该允许同时运行多少个操作,这在您有很多事情要做,但不能压倒一些外部资源.

In addition to its useful function, Promise.map() also supports a concurrency option that lets you specify how many operations should be allowed to be running at the same time which is particularly useful when you have a lot of something to do, but can't overwhelm some outside resource.

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

Some of these can be both called stand-alone and used on a promise that itself resolves to an iterable which can save a lot of code.

填充

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

In a browser project, since you generally want to still support some browsers that don't have Promise support, you end up needing a polyfill anyway. If you're also using jQuery, you can sometimes just use the promise support built into jQuery (though it is painfully non-standard in some ways, perhaps fixed in jQuery 3.0), but if the project involves any signficant async activity, I find the extended features in Bluebird very useful.

更快

另外值得注意的是,Bluebird 的 promise 似乎比 V8 中内置的 promise 快得多.请参阅这篇文章 有关该主题的更多讨论.

Also worth noting that Bluebird's promises appear to be significantly faster than the promises built into V8. See this post for more discussion on that topic.

缺少 Node.js 的一大亮点

让我考虑在 node.js 开发中少使用 Bluebird 的原因是,如果 node.js 内置了一个 promisify 函数,那么你可以做这样的事情:

What would make me consider using Bluebird less in node.js development would be if node.js built in a promisify function so you could do something like this:

const fs = requirep('fs');

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

或者只是提供已经承诺的方法作为内置模块的一部分.

Or just offer already promisified methods as part of the built-in modules.

在那之前,我用 Bluebird 做到这一点:

Until then, I do this with Bluebird:

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));

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

在 node.js 中内置 ES6 承诺支持并且没有任何内置模块返回承诺似乎有点奇怪.这需要在 node.js 中解决.在那之前,我使用 Bluebird 来保证整个库.所以,现在感觉 20% 的 promise 已经在 node.js 中实现了,因为没有一个内置模块让你在不先手动包装它们的情况下使用它们.

It seems a bit odd to have ES6 promise support built into node.js and have none of the built-in modules return promises. This needs to get sorted out in node.js. Until then, I use Bluebird to promisify whole libraries. So, it feels like promises are about 20% implemented in node.js now since none of the built-in modules let you use promises with them without manually wrapping them first.

这是一个简单的 Promise 与 Bluebird 的 promisify 和 Promise.map() 的示例,用于并行读取一组文件并在处理完所有数据后通知:

Here's an example of plain Promises vs. Bluebird's promisify and Promise.map() for reading a set of files in parallel and notifying when done with all the data:

简单的承诺

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
});

Bluebird Promise.map()Promise.promisifyAll()

Bluebird Promise.map() and Promise.promisifyAll()

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
});

<小时>

这是一个简单的 Promise 与 Bluebird 的 promisify 和 Promise.map() 的示例,当您从远程主机读取一堆 URL 时,您一次最多可以读取 4 个,但想要保持尽可能多的并行请求:


Here's an example of plain Promises vs. Bluebird's promisify and Promise.map() when reading a bunch of URLs from a remote host where you can read at most 4 at a time, but want to keep as many requests in parallel as allowed:

简单的 JS 承诺

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();
    });
}

蓝鸟承诺

const Promise = require('bluebird');
const request = Promise.promisifyAll(require('request'));
const urls = [url1, url2, url3, url4, url5, ....];

Promise.map(urls, request.getAsync, {concurrency: 4}).then(function(results) {
    // urls fetched in order in results Array
}, function(err) {
    // error here
});

这篇关于既然我们有 ES6 承诺,还有理由使用像 Q 或 BlueBird 这样的承诺库吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆