古老的格言是,您应该为工作选择正确的工具。 ES6 Promise 提供了基础。如果您想要或需要的只是基础知识,那么这对您来说应该/可以正常工作。但是,工具箱中的工具不仅仅是基础工具,而且在某些情况下这些附加工具非常有用。而且,我认为 ES6 Promise 甚至缺少一些基础知识,例如在几乎每个 node.js 项目中都很有用的 Promisification。
我对@987654321@ 最为熟悉,所以我将主要根据我使用该库的经验发言。
因此,这是我使用功能更强大的 Promise 库的 6 大理由
非承诺异步接口 - .promisify() 和 .promisifyAll() 对于处理仍然需要普通回调且尚未返回承诺的所有异步接口非常有用 - 一行代码创建整个界面的承诺版本。
更快 - 在大多数环境中,Bluebird 比原生承诺 significantly faster。
异步数组迭代的排序 - Promise.mapSeries() 或 Promise.reduce() 允许您遍历数组,对每个元素调用异步操作,但对异步操作进行排序以便它们发生一个接一个,而不是同时。您可以这样做,因为目标服务器需要它,或者因为您需要将一个结果传递给下一个结果。
Polyfill - 如果你想在旧版本的浏览器客户端中使用 Promise,无论如何你都需要一个 polyfill。也可以得到一个有能力的polyfill。由于 node.js 有 ES6 承诺,你不需要在 node.js 中使用 polyfill,但你可以在浏览器中使用。如果您同时编写 node.js 服务器和客户端,那么在两者中拥有相同的 Promise 库和功能可能非常有用(更易于共享代码、环境之间的上下文切换、对异步代码使用通用编码技术等。 .).
其他有用的功能 - Bluebird 有Promise.map()、Promise.some()、Promise.any()、Promise.filter()、Promise.each() 和Promise.props(),所有这些偶尔都很方便。虽然这些操作可以通过 ES6 Promise 和其他代码来执行,但 Bluebird 附带的这些操作已经预构建和预测试,因此使用起来更简单,代码更少。
内置警告和完整堆栈跟踪 - Bluebird 有许多内置警告,提醒您注意可能是错误代码或错误的问题。例如,如果您调用一个函数,该函数在 .then() 处理程序中创建了一个新的 Promise,但没有返回该 Promise(将其链接到当前的 Promise 链),那么在大多数情况下,这是一个意外错误,Bluebird 会给您一个对此发出警告。其他内置 Bluebird 警告是 described here。
以下是有关这些不同主题的更多详细信息:
PromisifyAll
在任何 node.js 项目中,我都会立即在任何地方使用 Bluebird,因为我在标准 node.js 模块(如 fs 模块)上经常使用 .promisifyAll()。
Node.js 本身并不为像 fs 模块那样执行异步 IO 的内置模块提供 Promise 接口。所以,如果你想在这些接口上使用 Promise,你要么在你使用的每个模块函数周围手动编写一个 Promise 包装器,要么获得一个可以为你做这件事的库,或者不使用 Promise。
Bluebird 的 Promise.promisify() 和 Promise.promisifyAll() 提供了对 node.js 调用约定异步 API 的自动包装以返回承诺。它非常有用且节省时间。我一直在使用它。
这是一个如何工作的示例:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
另一种方法是为您要使用的每个 fs API 手动创建自己的 Promise 包装器:
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() 就是这样一个实用程序。
其他有用的功能
以下是一些我特别认为有用的 Bluebird 功能(下面有几个代码示例,说明这些功能如何节省代码或加快开发速度):
Promise.promisify()
Promise.promisifyAll()
Promise.map()
Promise.reduce()
Promise.mapSeries()
Promise.delay()
除了有用的功能之外,Promise.map() 还支持一个并发选项,让您可以指定允许同时运行多少个操作,这在您有很多事情要做时特别有用,但是不能压倒一些外部资源。
其中一些既可以单独调用,也可以用于本身解析为可以节省大量代码的可迭代的 Promise。
Polyfill
在浏览器项目中,由于您通常仍希望支持一些不支持 Promise 的浏览器,因此您最终还是需要一个 polyfill。如果你也在使用 jQuery,你有时可以只使用 jQuery 内置的 Promise 支持(尽管它在某些方面非常不标准,可能在 jQuery 3.0 中修复),但如果项目涉及任何重要的异步活动,我发现Bluebird 中的扩展功能非常有用。
更快
另外值得注意的是,Bluebird 的 Promise 似乎比 V8 中内置的 Promise 快得多。有关该主题的更多讨论,请参阅this post。
缺少一个重要的 Node.js
让我考虑在 node.js 开发中少用 Bluebird 的原因是,如果 node.js 内置了一个 promisify 函数,那么你可以这样做:
const fs = requirep('fs');
fs.readFileAsync('somefile.text').then(function(data) {
// do something with data here
});
或者只是提供已经承诺的方法作为内置模块的一部分。
在那之前,我用 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 Promise 支持并且没有任何内置模块返回 Promise 似乎有点奇怪。这需要在 node.js 中解决。在那之前,我使用 Bluebird 来承诺整个库。因此,感觉现在 node.js 中实现了大约 20% 的 Promise,因为没有一个内置模块允许您在不先手动包装它们的情况下使用 Promise。
示例
这是一个简单的 Promises 与 Bluebird 的 promisify 和 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()
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
});
这是一个简单的 Promises 与 Bluebird 的 promisify 和 Promise.map() 的示例,当您从远程主机读取一堆 URL 时,您一次最多可以读取 4 个,但希望在允许的范围内保持尽可能多的并行请求:
纯 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
});