Javascript 既然我们有 ES6 承诺,还有理由使用 Q 或 BlueBird 之类的承诺库吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34960886/
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
Are there still reasons to use promise libraries like Q or BlueBird now that we have ES6 promises?
提问by Murat Ozgul
After Node.js added native support for promises, are there still reasons to use libraries like Q or BlueBird?
在 Node.js 添加对 promise 的原生支持后,还有理由使用 Q 或 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?
例如,如果您正在启动一个新项目,并且假设在该项目中您没有任何使用这些库的依赖项,我们是否可以说真的没有更多理由使用这些库?
回答by jfriend00
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.
古老的格言说,您应该为工作选择合适的工具。ES6 承诺提供了基础。如果您想要或需要的只是基础知识,那么这应该/可以为您工作。但是,工具箱中的工具不仅仅是基础工具,而且在某些情况下,这些附加工具非常有用。而且,我认为 ES6 的 promises 甚至缺少一些基本的东西,比如在几乎每个 node.js 项目中都有用的 promisification。
I'm most familiar with the Bluebird promise libraryso I'll speak mostly from my experience with that library.
我最熟悉Bluebird promise 库,所以我将主要根据我对该库的经验进行发言。
So, here are my top 6 reasons to use a more capable Promise library
所以,这是我使用功能更强大的 Promise 库的 6 大理由
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.Faster- Bluebird is significantly fasterthan native promises in most environments.
Sequencing of async array iteration-
Promise.mapSeries()
orPromise.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- 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...).
Other Useful Features- Bluebird has
Promise.map()
,Promise.some()
,Promise.any()
,Promise.filter()
,Promise.each()
andPromise.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.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.
非Promisified异步接口-
.promisify()
并且.promisifyAll()
是非常有用的,以处理所有仍然需要简单的回调和尚未返回承诺的异步接口-一行代码创建整个界面的promisified版本。更快-在大多数环境中,Bluebird比原生 promise快得多。
异步数组迭代的排序-
Promise.mapSeries()
或者Promise.reduce()
允许您遍历数组,对每个元素调用异步操作,但对异步操作进行排序,以便它们一个接一个地发生,而不是同时发生。您可以这样做,因为目标服务器需要它,或者因为您需要将一个结果传递给下一个。Polyfill- 如果您想在旧版本的浏览器客户端中使用 promise,无论如何您都需要一个 polyfill。也可以得到一个有能力的 polyfill。由于 node.js 具有 ES6 承诺,因此您不需要在 node.js 中使用 polyfill,但您可以在浏览器中使用。如果您同时编写 node.js 服务器和客户端,那么在两者中使用相同的 Promise 库和功能可能非常有用(更容易共享代码、环境之间的上下文切换、对异步代码使用通用编码技术等。 .)
其它实用功能-蓝鸟有
Promise.map()
,Promise.some()
,Promise.any()
,Promise.filter()
,Promise.each()
和Promise.props()
所有这一切都是偶然得心应手。虽然这些操作可以使用 ES6 承诺和附加代码执行,但 Bluebird 附带这些操作已经预先构建和预先测试,因此使用它们更简单,代码更少。内置警告和完整堆栈跟踪- Bluebird 具有许多内置警告,可提醒您注意可能是错误代码或错误的问题。例如,如果您调用一个在
.then()
处理程序中创建新承诺的函数而不返回该承诺(将其链接到当前的承诺链),那么在大多数情况下,这是一个意外错误,Bluebird 会对此发出警告影响。此处描述了其他内置的 Bluebird 警告。
Here's some more detail on these various topics:
以下是有关这些不同主题的更多详细信息:
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 项目中,我都会立即在任何地方使用 Bluebird,因为我.promisifyAll()
在标准 node.js 模块(如fs
模块)上使用了很多。
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.
Node.js 本身并不像模块那样为执行异步 IO 的内置模块提供 promise 接口fs
。因此,如果您想在这些接口中使用 Promise,您要么为您使用的每个模块函数手动编写一个 Promise 包装器,要么获取一个可以为您执行此操作的库,或者不使用 Promise。
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.
BluebirdPromise.promisify()
并Promise.promisifyAll()
提供自动包装 node.js 调用约定异步 API 以返回承诺。这是非常有用和节省时间的。我用它所有的时间。
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
});
The alternative would be to manually create your own promise wrapper for each fs
API you wanted to use:
另一种方法是为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
});
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.
而且,您必须为要使用的每个 API 函数手动执行此操作。这显然没有意义。这是样板代码。您不妨得到一个实用程序来为您完成这项工作。Bluebird'sPromise.promisify()
和Promise.promisifyAll()
就是这样一个实用程序。
Other Useful Features
其他有用的功能
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):
以下是我特别觉得有用的一些 Bluebird 功能(下面有几个代码示例,介绍了这些功能如何节省代码或加快开发速度):
Promise.promisify()
Promise.promisifyAll()
Promise.map()
Promise.reduce()
Promise.mapSeries()
Promise.delay()
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.
除了有用的功能外,Promise.map()
还支持一个并发选项,让你指定应该允许同时运行多少个操作,这在你有很多事情要做,但又不能压倒一些外面的时候特别有用资源。
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.
其中一些既可以称为独立的,也可以用于承诺本身,该承诺本身解析为可迭代的,可以节省大量代码。
Polyfill
填充物
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.
在浏览器项目中,由于您通常希望仍然支持一些没有 Promise 支持的浏览器,因此无论如何您最终都需要一个 polyfill。如果你也使用 jQuery,你有时可以只使用 jQuery 内置的 promise 支持(尽管它在某些方面非常不标准,可能在 jQuery 3.0 中修复),但如果项目涉及任何重要的异步活动,我发现Bluebird 中的扩展功能非常有用。
Faster
快点
Also worth noting that Bluebird's promises appear to be significantly faster than the promises built into V8. See this postfor more discussion on that topic.
同样值得注意的是,Bluebird 的 promise 似乎比 V8 中内置的 promise 快得多。有关该主题的更多讨论,请参阅此帖子。
A Big Thing Node.js is Missing
Node.js 缺失的一件大事
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:
让我考虑在 node.js 开发中少使用 Bluebird 的原因是,如果 node.js 内置了一个 promisify 函数,那么你可以做这样的事情:
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.
或者只是提供已经承诺的方法作为内置模块的一部分。
Until then, I do this with Bluebird:
在此之前,我使用 Bluebird 执行此操作:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
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.
在 node.js 中内置 ES6 承诺支持并且没有任何内置模块返回承诺似乎有点奇怪。这需要在 node.js 中解决。在那之前,我使用 Bluebird 来保证整个库。因此,感觉现在在 node.js 中实现了大约 20% 的 promise,因为没有一个内置模块允许您在不先手动包装它们的情况下使用它们。
Examples
例子
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:
这是一个简单的 Promise 与 Bluebird 的 promisify 的示例,Promise.map()
用于并行读取一组文件并在处理完所有数据后通知:
Plain Promises
简单的承诺
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()
and Promise.promisifyAll()
蓝鸟Promise.map()
和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
});
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:
这是一个简单的 Promise 与 Bluebird 的 promisify 的例子,Promise.map()
当从远程主机读取一堆 URL 时,您一次最多可以读取 4 个,但希望保持尽可能多的并行请求:
Plain JS Promises
简单的 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();
});
}
Bluebird Promises
蓝鸟承诺
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
});